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 f59aaaba2c0b31acca4d6471f533c7f5e9466bb2 Author: Yurii Tykhun <[email protected]> AuthorDate: Thu Jul 29 13:59:47 2021 +0300 [DATALAB-2347] refactored Manegement component --- .../backup-dilog/backup-dilog.component.html | 41 ++- .../backup-dilog/backup-dilog.component.ts | 41 +-- .../management/endpoints/endpoints.component.html | 122 +++++--- .../management/endpoints/endpoints.component.scss | 24 +- .../management/endpoints/endpoints.component.ts | 103 ++++--- .../manage-environment-dilog.component.html | 99 +++++-- .../manage-environment-dilog.component.scss | 8 +- .../manage-environment-dilog.component.ts | 34 ++- .../management/management-data.service.ts | 6 +- .../management-grid/management-grid.component.html | 330 +++++++++++++++------ .../management-grid/management-grid.component.scss | 113 ++++--- .../management-grid/management-grid.component.ts | 112 ++++--- .../management/management.component.html | 59 ++-- .../management/management.component.ts | 128 ++++---- .../administration/management/management.model.ts | 1 - .../ssn-monitor/ssn-monitor.component.html | 9 +- .../ssn-monitor/ssn-monitor.component.scss | 8 +- .../ssn-monitor/ssn-monitor.component.ts | 7 +- 18 files changed, 801 insertions(+), 444 deletions(-) diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/backup-dilog/backup-dilog.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/backup-dilog/backup-dilog.component.html index 545c236..3b3c9f7 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/backup-dilog/backup-dilog.component.html +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/backup-dilog/backup-dilog.component.html @@ -20,7 +20,11 @@ <div class="backup-dialog" id="dialog-box"> <header class="dialog-header"> <h4 class="modal-title">Backup options</h4> - <button type="button" class="close" (click)="dialogRef.close()">×</button> + <button + type="button" + class="close" + (click)="dialogRef.close()" + >×</button> </header> <div class="dialog-content"> <div id="backup-options" class="content-box" *ngIf="backupOptions"> @@ -28,7 +32,8 @@ <mat-slide-toggle labelPosition="before" [checked]="backupOptions.configFiles[0] === 'all'" - (change)="onHoldChanged($event, 'configFiles')"> + (change)="onHoldChanged($event, 'configFiles')" + > <span class="hold-label">Configuration files</span> </mat-slide-toggle> </div> @@ -36,7 +41,8 @@ <mat-slide-toggle labelPosition="before" [checked]="backupOptions.keys[0] === 'all'" - (change)="onHoldChanged($event, 'keys')"> + (change)="onHoldChanged($event, 'keys')" + > <span class="hold-label">User keys</span> </mat-slide-toggle> </div> @@ -44,7 +50,8 @@ <mat-slide-toggle labelPosition="before" [checked]="backupOptions.databaseBackup" - (change)="onHoldChanged($event, 'databaseBackup')"> + (change)="onHoldChanged($event, 'databaseBackup')" + > <span class="hold-label">Database</span> </mat-slide-toggle> </div> @@ -52,7 +59,8 @@ <mat-slide-toggle labelPosition="before" [checked]="backupOptions.certificates[0] === 'all'" - (change)="onHoldChanged($event, 'certificates')"> + (change)="onHoldChanged($event, 'certificates')" + > <span class="hold-label">SSL Certificates</span> </mat-slide-toggle> </div> @@ -60,7 +68,8 @@ <mat-slide-toggle labelPosition="before" [checked]="backupOptions.jars[0] === 'all'" - (change)="onHoldChanged($event, 'jars')"> + (change)="onHoldChanged($event, 'jars')" + > <span class="hold-label">JAR files</span> </mat-slide-toggle> </div> @@ -68,15 +77,29 @@ <mat-slide-toggle labelPosition="before" [checked]="backupOptions.logsBackup" - (change)="onHoldChanged($event, 'logsBackup')"> + (change)="onHoldChanged($event, 'logsBackup')" + > <span class="hold-label">Log files</span> </mat-slide-toggle> </div> <div class="text-center m-top-30 m-bott-10"> - <button mat-raised-button type="button" class="butt" (click)="dialogRef.close(); backupOptions.setDegault()"> + <button + mat-raised-button + type="button" + class="butt" + (click)="dialogRef.close(); backupOptions.setDegault()" + > Cancel </button> - <button mat-raised-button type="button" (click)="applyOptions()" class="butt butt-success" [disabled]="!valid">Apply</button> + <button + mat-raised-button + type="button" + class="butt butt-success" + (click)="applyOptions()" + [disabled]="!valid" + > + Apply + </button> </div> </div> </div> diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/backup-dilog/backup-dilog.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/backup-dilog/backup-dilog.component.ts index fd7b4bd..3ee9f7d 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/backup-dilog/backup-dilog.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/backup-dilog/backup-dilog.component.ts @@ -18,10 +18,10 @@ */ import { Component, OnInit, Output, EventEmitter, Inject } from '@angular/core'; -import { DICTIONARY } from '../../../../dictionary/global.dictionary'; import { MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; +import { DICTIONARY } from '../../../../dictionary/global.dictionary'; import { BackupOptionsModel } from '../management.model'; import { BackupService } from '../../../core/services'; @@ -54,18 +54,20 @@ export class BackupDilogComponent implements OnInit { this.backupOptions[key] instanceof Array ? (this.backupOptions[key][0] = $event.checked ? 'all' : 'skip') : (this.backupOptions[key] = !this.backupOptions[key]); - this.checkValidity(); } public applyOptions(): void { - this.backupService.createBackup(this.backupOptions).subscribe(result => { - this.getBackupStatus(result); - this.toastr.success('Backup configuration is processing!', 'Processing!'); - this.clear = window.setInterval(() => this.getBackupStatus(result), 3000); - this.dialogRef.close(this.backupOptions); - }, - error => this.toastr.error(error.message, 'Oops!')); + this.backupService.createBackup(this.backupOptions) + .subscribe( + result => { + this.getBackupStatus(result); + this.toastr.success('Backup configuration is processing!', 'Processing!'); + this.clear = window.setInterval(() => this.getBackupStatus(result), 3000); + this.dialogRef.close(this.backupOptions); + }, + error => this.toastr.error(error.message, 'Oops!') + ); } private checkValidity(): void { @@ -85,17 +87,20 @@ export class BackupDilogComponent implements OnInit { private getBackupStatus(result) { const uuid = result.body; this.backupService.getBackupStatus(uuid) - .subscribe((backupStatus: any) => { - if (!this.creatingBackup) { - backupStatus.status === 'FAILED' - ? this.toastr.error('Backup configuration failed!', 'Oops!') - : this.toastr.success('Backup configuration completed!', 'Success!'); + .subscribe( + (backupStatus: any) => { + if (!this.creatingBackup) { + backupStatus.status === 'FAILED' + ? this.toastr.error('Backup configuration failed!', 'Oops!') + : this.toastr.success('Backup configuration completed!', 'Success!'); + clearInterval(this.clear); + } + }, + () => { clearInterval(this.clear); + this.toastr.error('Backup configuration failed!', 'Oops!'); } - }, () => { - clearInterval(this.clear); - this.toastr.error('Backup configuration failed!', 'Oops!'); - }); + ); } get creatingBackup(): boolean { diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.html index 2181f9b..f198e53 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.html +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.html @@ -21,7 +21,11 @@ <div class="endpoints-dialog" id="dialog-box"> <header class="dialog-header"> <h4 class="modal-title">Connect endpoint</h4> - <button type="button" class="close" (click)="dialogRef.close()">×</button> + <button + type="button" + class="close" + (click)="dialogRef.close()" + >×</button> </header> <div class="dialog-content tabs"> <div class="content-box"> @@ -32,24 +36,33 @@ <div class="control-group"> <label class="label">Name</label> <div class="control"> - <input type="text" formControlName="name" placeholder="Enter endpoint name" - (blur)="generateEndpointTag($event)"> - <span class="error" + <input + type="text" + formControlName="name" + placeholder="Enter endpoint name" + (blur)="generateEndpointTag($event)" + /> + <span + class="error" *ngIf="!createEndpointForm?.controls.name.valid - && createEndpointForm?.controls.name.touched - && !createEndpointForm?.controls['name'].hasError('isDuplicate') - && !createEndpointForm?.controls['name'].hasError('limit')" + && createEndpointForm?.controls.name.touched + && !createEndpointForm?.controls['name'].hasError('isDuplicate') + && !createEndpointForm?.controls['name'].hasError('limit')" > Endpoint name can only contain letters, numbers, hyphens and '_' but can not end with special characters. </span> - <span class="error" - *ngIf="createEndpointForm?.controls['name'].hasError('isDuplicate')"> + <span + class="error" + *ngIf="createEndpointForm?.controls['name'].hasError('isDuplicate')" + > This endpoint name already exists. </span> - <span class="error" - *ngIf="createEndpointForm?.controls['name'].hasError('limit')"> - Endpoint name cannot be longer than {{ maxEndpointNameLength }} characters. + <span + class="error" + *ngIf="createEndpointForm?.controls['name'].hasError('limit')" + > + Endpoint name cannot be longer than {{ maxEndpointNameLength }} characters. </span> </div> </div> @@ -58,8 +71,12 @@ <div class="control"> <input type="text" formControlName="url" placeholder="Enter endpoint url"> <span class="error" *ngIf="createEndpointForm?.controls['url'].hasError('isDuplicate')">This endpoint url already exists.</span> - <span class="error" - *ngIf="!createEndpointForm?.controls.url.valid && createEndpointForm.controls.url.touched && !createEndpointForm?.controls['url'].hasError('isDuplicate')"> + <span + class="error" + *ngIf="!createEndpointForm?.controls.url.valid + && createEndpointForm.controls.url.touched + && !createEndpointForm?.controls['url'].hasError('isDuplicate')" + > Please provide a valid endpoint url with slash in the end. </span> </div> @@ -68,8 +85,10 @@ <label class="label">Account</label> <div class="control"> <input type="text" formControlName="account" placeholder="Enter account"> - <span class="error" - *ngIf="!createEndpointForm?.controls.account.valid && createEndpointForm.controls.account.touched"> + <span + class="error" + *ngIf="!createEndpointForm?.controls.account.valid && createEndpointForm.controls.account.touched" + > Endpoint account can only contain letters, numbers, hyphens and '_' but can not end with special characters. </span> @@ -78,10 +97,18 @@ <div class="control-group"> <label class="label">Endpoint tag</label> <div class="control"> - <input readonly type="text" formControlName="endpoint_tag" placeholder="< equal to endpoint name >" - class="not-allowed"> - <span class="error" - *ngIf="!createEndpointForm?.controls.endpoint_tag.valid && createEndpointForm.controls.endpoint_tag.touched"> + <input + readonly + type="text" + formControlName="endpoint_tag" + placeholder="< equal to endpoint name >" + class="not-allowed" + /> + <span + class="error" + *ngIf="!createEndpointForm?.controls.endpoint_tag.valid + && createEndpointForm.controls.endpoint_tag.touched" + > Endpoint tag can only contain letters, numbers, hyphens and '_' but can not end with special characters. </span> @@ -89,25 +116,45 @@ </div> </form> <div class="action-group m-bott-10"> - <button mat-raised-button - type="button" - [disabled]="!this.createEndpointForm.value.url || !createEndpointForm.valid" - class="butt action" - (click)="getEndpoinConnectionStatus(createEndpointForm.value.url)" + <button + mat-raised-button + type="button" + [disabled]="!this.createEndpointForm.value.url || !createEndpointForm.valid" + class="butt action" + (click)="getEndpoinConnectionStatus(createEndpointForm.value.url)" > Test </button> <div class="action-butt"> - <button mat-raised-button type="button" class="butt action" (click)="dialogRef.close()">Cancel</button> - <button mat-raised-button type="button" [disabled]="!createEndpointForm.valid" - (click)="assignChanges(createEndpointForm.value)" class="butt butt-success action">Connect</button> + <button + mat-raised-button + type="button" + class="butt action" + (click)="dialogRef.close()" + > + Cancel + </button> + <button + mat-raised-button + type="button" + [disabled]="!createEndpointForm.valid" + (click)="assignChanges(createEndpointForm.value)" + class="butt butt-success action" + > + Connect + </button> </div> </div> </div> </mat-tab> <mat-tab label="ENDPOINTS LIST"> <div class="endpoints scrolling"> - <table mat-table [dataSource]="endpoints" class="endpoints-table" *ngIf="endpoints?.length; else empty"> + <table + mat-table + [dataSource]="endpoints" + class="endpoints-table" + *ngIf="endpoints?.length; else empty" + > <ng-container matColumnDef="name"> <th mat-header-cell *matHeaderCellDef class="name"> Endpoint name </th> <td mat-cell *matCellDef="let element"> {{element.name}} </td> @@ -115,9 +162,7 @@ <ng-container matColumnDef="url"> <th mat-header-cell *matHeaderCellDef class="url"> Url </th> - <td mat-cell - *matCellDef="let element" - > + <td mat-cell *matCellDef="let element"> <span matTooltip="{{element.url}}" matTooltipPosition="above">{{element.url}}</span> </td> </ng-container> @@ -135,10 +180,18 @@ <ng-container matColumnDef="actions"> <th mat-header-cell *matHeaderCellDef class="actions"></th> <td mat-cell *matCellDef="let element" class="actions"> - <span (click)="getEndpoinConnectionStatus(element.url)" matTooltip="Test" matTooltipPosition="above"> + <span + matTooltip="Test" + matTooltipPosition="above" + (click)="getEndpoinConnectionStatus(element.url)" + > <mat-icon>compare_arrows</mat-icon> </span> - <span (click)="deleteEndpoint(element)" matTooltip="Disconnect" matTooltipPosition="above"> + <span + matTooltip="Disconnect" + matTooltipPosition="above" + (click)="deleteEndpoint(element)" + > <mat-icon>delete_forever</mat-icon> </span> </td> @@ -152,7 +205,6 @@ <div class="info empty-box"> <div class="content"> <p>Looks like you don't have any endpoints</p> - <button mat-raised-button class="butt" (click)="tabGroup.selectedIndex = 0"> <i class="material-icons">settings_system_daydream</i>New endpoint </button> diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.scss index 53eaaf6..520faf7 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.scss +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.scss @@ -18,8 +18,8 @@ */ .endpoints-dialog { .content-box { - padding: 10px 30px 30px; height: 400px; + padding: 10px 30px 30px; .split { display: flex; @@ -35,9 +35,9 @@ position: absolute; right: 0; bottom: 5px; - font-family: 'Open Sans', sans-serif; - font-weight: 300; top: 34px; + font-family: "Open Sans", sans-serif; + font-weight: 300; } } } @@ -56,37 +56,37 @@ table.mat-table { width: 100%; - thead{ + thead { background: transparent; - .mat-header-row{ - background-clip:padding-box; + .mat-header-row { + background-clip: padding-box; } } tr { td { - vertical-align: middle; max-width: 150px; + vertical-align: middle; overflow: hidden; text-overflow: ellipsis; - &::after{ - content: ''; + &::after { + content: ""; display: block; } } } .actions { + width: 14%; color: #607d8b; text-align: center; - width: 14%; span { - transition: all .5s ease-in-out; + transition: all 0.5s ease-in-out; cursor: pointer; .mat-icon { - font-size: 18px; padding-top: 5px; + font-size: 18px; } &:hover { diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.ts index 5f13786..0dfffeb 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/endpoints/endpoints.component.ts @@ -21,11 +21,11 @@ import { Component, OnInit, Inject } from '@angular/core'; import { FormGroup, FormBuilder, Validators } from '@angular/forms'; import { MatDialogRef, MAT_DIALOG_DATA, MatDialog } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; +import { map } from 'rxjs/operators'; import { EndpointService } from '../../../core/services'; import { NotificationDialogComponent } from '../../../shared/modal-dialog/notification-dialog'; import { PATTERNS } from '../../../core/util'; -import { map } from 'rxjs/operators'; export interface Endpoint { name: string; @@ -71,51 +71,75 @@ export class EndpointsComponent implements OnInit { public deleteEndpoint(data): void { this.endpointService.getEndpointsResource(data.name) - .pipe(map(resource => - resource.projects.map(project => - EndpointsComponent.createResourceList( - project.name, - resource.exploratories.filter(notebook => notebook.project === project.name), - project.endpoints.filter(endpoint => endpoint.name === data.name)[0].status)) - .filter(project => project.nodeStatus !== 'TERMINATED' - && project.nodeStatus !== 'TERMINATING' - && project.nodeStatus !== 'FAILED' - ))) + .pipe( + map(resource => + resource.projects + .map(project => + EndpointsComponent.createResourceList( + project.name, + resource.exploratories.filter(notebook => notebook.project === project.name), + project.endpoints.filter(endpoint => endpoint.name === data.name)[0].status) + ) + .filter(project => project.nodeStatus !== 'TERMINATED' + && project.nodeStatus !== 'TERMINATING' + && project.nodeStatus !== 'FAILED' + ) + ) + ) .subscribe((resource: any) => { - this.dialog.open(NotificationDialogComponent, { data: { - type: 'confirmation', item: data, list: resource - }, panelClass: 'modal-sm' }) - .afterClosed().subscribe(result => { - result && this.deleteEndpointOption(data); - }); - }); + this.dialog.open(NotificationDialogComponent, { + data: { + type: 'confirmation', item: data, list: resource + }, panelClass: 'modal-sm' + }) + .afterClosed().subscribe(result => { + result && this.deleteEndpointOption(data); + }); + }); } public getEndpoinConnectionStatus(url) { const getStatus = this.endpointService.getEndpoinConnectionStatus(encodeURIComponent(url)); - this.dialog.open(EndpointTestResultDialogComponent, { data: {url: url, getStatus}, panelClass: 'modal-sm' }); + this.dialog.open(EndpointTestResultDialogComponent, { data: { url: url, getStatus }, panelClass: 'modal-sm' }); } private static createResourceList(name: string, resource: Array<any>, nodeStatus: string): Object { - return {name, resource, nodeStatus}; + return { name, resource, nodeStatus }; } private initFormModel(): void { this.createEndpointForm = this._fb.group({ name: ['', Validators.compose([ - Validators.required, Validators.pattern(PATTERNS.namePattern), this.validateName.bind(this), this.providerMaxLength.bind(this) + Validators.required, + Validators.pattern(PATTERNS.namePattern), + this.validateName.bind(this), + this.providerMaxLength.bind(this) ])], - url: ['', Validators.compose([Validators.required, Validators.pattern(PATTERNS.fullUrl), this.validateUrl.bind(this)])], - account: ['', Validators.compose([Validators.required, Validators.pattern(PATTERNS.namePattern)])], - endpoint_tag: ['', Validators.compose([Validators.required, Validators.pattern(PATTERNS.namePattern)])] + url: ['', Validators.compose([ + Validators.required, + Validators.pattern(PATTERNS.fullUrl), + this.validateUrl.bind(this) + ])], + account: ['', Validators.compose([ + Validators.required, + Validators.pattern(PATTERNS.namePattern) + ])], + endpoint_tag: ['', Validators.compose([ + Validators.required, + Validators.pattern(PATTERNS.namePattern) + ])] }); } private deleteEndpointOption(data): void { - this.endpointService.deleteEndpoint(`${data.name}`).subscribe(() => { - this.toastr.success( 'Endpoint successfully disconnected. All related resources are terminating!', 'Success!'); - this.getEndpointList(); - }, error => this.toastr.error(error.message || 'Endpoint creation failed!', 'Oops!')); + this.endpointService.deleteEndpoint(`${data.name}`) + .subscribe( + () => { + this.toastr.success('Endpoint successfully disconnected. All related resources are terminating!', 'Success!'); + this.getEndpointList(); + }, + error => this.toastr.error(error.message || 'Endpoint creation failed!', 'Oops!') + ); } private getEndpointList(): void { @@ -218,19 +242,20 @@ export class EndpointTestResultDialogComponent { public dialogRef: MatDialogRef<EndpointTestResultDialogComponent>, @Inject(MAT_DIALOG_DATA) public data: any ) { - this.data.getStatus.subscribe(() => { - this.isConnected = true; - this.response = true; - return; - }, - () => { - this.isConnected = false; - this.response = true; - return; - }); + this.data.getStatus + .subscribe( + () => { + this.isConnected = true; + this.response = true; + return; + }, + () => { + this.isConnected = false; + this.response = true; + return; + }); } public cutToLongUrl(url) { return url.length > 25 ? url.slice(0, 25) + '...' : url; } - } diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.html index 8c879af..6e2b288 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.html +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.html @@ -25,7 +25,11 @@ <div class="dialog-content"> <div class="content-box"> <div *ngIf="data.projectsList?.length"> - <form [formGroup]="manageUsersForm" (submit)="setBudgetLimits(manageUsersForm.value)" novalidate> + <form + [formGroup]="manageUsersForm" + (submit)="setBudgetLimits(manageUsersForm.value)" + novalidate + > <mat-list> <mat-list-item class="list-header"> <div class="username">Project</div> @@ -33,54 +37,93 @@ <div class="quotes">Limit</div> </mat-list-item> <div class="scrolling-content" id="scrolling" formArrayName="projects"> - <mat-list-item *ngFor="let item of usersEnvironments.controls; let i=index" [formGroupName]="i" - class="list-item"> + <mat-list-item + *ngFor="let item of usersEnvironments.controls; let i=index" + [formGroupName]="i" + class="list-item" + > <div class="username ellipsis"> - <span class="ellipsis" - matTooltip="{{ manageUsersForm.controls['projects']['controls'][i].value['project'] }}" - matTooltipPosition="above">{{ manageUsersForm.controls['projects']['controls'][i].value['project'] }} - </span> + <span + class="ellipsis" + matTooltip="{{ manageUsersForm.controls['projects']['controls'][i].value['project'] }}" + matTooltipPosition="above" + > + {{ manageUsersForm.controls['projects']['controls'][i].value['project'] }} + </span> </div> <div class="period"> - <mat-slide-toggle formControlName="monthlyBudget"> - </mat-slide-toggle> + <mat-slide-toggle formControlName="monthlyBudget"></mat-slide-toggle> </div> <div class="quotes"> - <input type="number" (keypress)="CheckUtils.numberOnly($event)" min="0" - placeholder="Enter limit, in USD" formControlName="budget"> - <span class="error" - *ngIf="manageUsersForm?.controls['projects']['controls'][i].controls['budget'].hasError('overrun') && - !manageUsersForm?.controls['projects']['controls'][i].controls['budget'].hasError('max')" + <input + type="number" + (keypress)="CheckUtils.numberOnly($event)" + min="0" + placeholder="Enter limit, in USD" formControlName="budget" + /> + <span + class="error" + *ngIf="manageUsersForm?.controls['projects']['controls'][i].controls['budget'].hasError('overrun') + && !manageUsersForm?.controls['projects']['controls'][i].controls['budget'].hasError('max')" > Projects budget cannot be higher than total budget. </span> - <span class="error" - *ngIf="manageUsersForm?.controls['projects']['controls'][i].controls['budget'].hasError('max')">Project budget cannot be higher than 1000000000.</span> + <span + class="error" + *ngIf="manageUsersForm?.controls['projects']['controls'][i].controls['budget'].hasError('max')" + > + Project budget cannot be higher than 1000000000. + </span> </div> </mat-list-item> </div> <div class="control-group total-budget"> - <mat-list-item class="list-item"> <div class="username ellipsis"> <span class="ellipsis">Total budget</span> </div> - <div class="period"> - </div> + <div class="period"></div> <div class="quotes"> - <input type="number" (keypress)="CheckUtils.numberOnly($event)" formControlName="total" - placeholder="Enter total budget, in USD"> - <span class="error" *ngIf="manageUsersForm?.controls['total'].hasError('overrun') - && !manageUsersForm?.controls['total'].hasError('max')">Total budget cannot be lower than a sum of project quotes.</span> - <span class="error" - *ngIf="manageUsersForm?.controls['total'].hasError('max')">Total budget cannot be higher than 1000000000.</span> + <input + type="number" + (keypress)="CheckUtils.numberOnly($event)" + formControlName="total" + placeholder="Enter total budget, in USD" + /> + <span + class="error" + *ngIf="manageUsersForm?.controls['total'].hasError('overrun') + && !manageUsersForm?.controls['total'].hasError('max')" + > + Total budget cannot be lower than a sum of project quotes. + </span> + <span + class="error" + *ngIf="manageUsersForm?.controls['total'].hasError('max')" + > + Total budget cannot be higher than 1000000000. + </span> </div> </mat-list-item> </div> <div class="text-center m-top-30"> - <button mat-raised-button type="button" (click)="dialogRef.close()" class="butt action">Cancel</button> - <button mat-raised-button type="submit" [disabled]="!manageUsersForm.valid || isFormChanged" class="butt butt-success" - [ngClass]="{'not-allowed': !manageUsersForm.valid}">Apply</button> + <button + mat-raised-button + type="button" + (click)="dialogRef.close()" + class="butt action" + > + Cancel + </button> + <button + mat-raised-button + type="submit" + [disabled]="!manageUsersForm.valid || isFormChanged" + class="butt butt-success" + [ngClass]="{'not-allowed': !manageUsersForm.valid}" + > + Apply + </button> </div> </mat-list> </form> diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.scss index 1cea435..d04b101 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.scss +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.scss @@ -20,9 +20,9 @@ .manage-env-dialog { .mat-list { .mat-list-item { + position: relative; height: 63px; margin: 5px 0; - position: relative; .mat-list-item-content { padding: 0 !important; @@ -43,20 +43,20 @@ } .quotes { + position: relative; width: 45%; margin-right: 10px; - position: relative; .error { position: absolute; left: 0; top: 37px; - font-family: 'Open Sans', sans-serif; + font-family: "Open Sans", sans-serif; font-size: 11px; } } - .period{ + .period { width: 25%; } diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.ts index 6fd04c9..2c154ef 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/manage-environment/manage-environment-dilog.component.ts @@ -20,6 +20,7 @@ import { Component, Output, EventEmitter, ViewEncapsulation, Inject, OnInit } from '@angular/core'; import { Validators, FormBuilder, FormGroup, FormArray } from '@angular/forms'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; + import { DICTIONARY } from '../../../../dictionary/global.dictionary'; import { CheckUtils } from '../../../core/util'; @@ -63,12 +64,12 @@ export class ManageEnvironmentComponent implements OnInit { if (this.getCurrentTotalValue()) { if (this.getCurrentTotalValue() >= this.getCurrentUsersTotal()) { this.manageUsersForm.controls['total'].setErrors(null); - if (this.manageUsersForm.controls['total'].value > 1000000000) this.manageUsersForm.controls['total'].setErrors({max: true}); + if (this.manageUsersForm.controls['total'].value > 1000000000) this.manageUsersForm.controls['total'].setErrors({ max: true }); this.manageUsersForm.controls['projects']['controls'].forEach(v => { - v.controls['budget'].errors && - 'max' in v.controls['budget'].errors ? null : v.controls['budget'].setErrors(null); - } - ); + v.controls['budget'].errors && 'max' in v.controls['budget'].errors + ? null + : v.controls['budget'].setErrors(null); + }); } else { this.manageUsersForm.controls['total'].setErrors({ overrun: true }); } @@ -85,8 +86,8 @@ export class ManageEnvironmentComponent implements OnInit { value.projects = value.projects.filter((v, i) => this.initialFormState.projects[i].budget !== v.budget || this.initialFormState.projects[i].monthlyBudget !== v.monthlyBudget); - value.isTotalChanged = this.initialFormState.total !== value.total; - this.dialogRef.close(value); + value.isTotalChanged = this.initialFormState.total !== value.total; + this.dialogRef.close(value); } else { this.manageUsersForm.controls['total'].setErrors({ overrun: true }); } @@ -94,16 +95,19 @@ export class ManageEnvironmentComponent implements OnInit { public setProjectsControl() { this.manageUsersForm.setControl('projects', - this._fb.array((this.data.projectsList || []).map((x: any, index: number) => this._fb.group({ - project: x.name, - budget: [x.budget.value, [ Validators.max(1000000000), this.userValidityCheck.bind(this)]], - monthlyBudget: x.budget.monthlyBudget, - })))); + this._fb.array((this.data.projectsList || []) + .map((x: any, index: number) => this._fb.group({ + project: x.name, + budget: [x.budget.value, [Validators.max(1000000000), this.userValidityCheck.bind(this)]], + monthlyBudget: x.budget.monthlyBudget, + })) + ) + ); } private initForm(): void { this.manageUsersForm = this._fb.group({ - total: [null, [Validators.min(0), this.totalValidityCheck.bind(this), Validators.max(1000000000) ]], + total: [null, [Validators.min(0), this.totalValidityCheck.bind(this), Validators.max(1000000000)]], projects: this._fb.array([this._fb.group({ project: '', budget: null, status: '' })]) }); } @@ -124,7 +128,9 @@ export class ManageEnvironmentComponent implements OnInit { private userValidityCheck(control) { if (control && control.value) { - if (control.parent)this.manageUsersForm.value.projects.find(v => v.project === control.parent.value.project).budget = control.value; + if (control.parent) { + this.manageUsersForm.value.projects.find(v => v.project === control.parent.value.project).budget = control.value; + } return (this.getCurrentTotalValue() && this.getCurrentTotalValue() < this.getCurrentUsersTotal()) ? { overrun: true } : null; } } diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-data.service.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-data.service.ts index a3aa218..5779249 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management-data.service.ts +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management-data.service.ts @@ -40,7 +40,9 @@ export class EnvironmentsDataService { } private getAllEnvironmentData() { - this.manageEnvironmentsService.getAllEnvironmentData().subscribe( - (response) => this._data.next(response)); + this.manageEnvironmentsService.getAllEnvironmentData() + .subscribe( + (response) => this._data.next(response) + ); } } \ No newline at end of file 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 8686e4b..54d9b2c 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 @@ -27,22 +27,25 @@ *ngIf="allActiveNotebooks?.length" [checked]="selected?.length === allActiveNotebooks?.length" (toggleSelection)="toggleSelectionAll()" - > - </datalab-checkbox> + ></datalab-checkbox> <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 *ngIf="filtering && filterForm.users.length > 0 && !collapsedFilterRow">filter_list</span> --> <span>more_vert</span> </i> </button> </th> <td mat-cell *matCellDef="let element"> - <ng-template [ngIf]="element.type !== 'odahu' && element.type !== 'edge node' && (element.status==='running' || element.status==='stopped') && !clustersInProgress(element.resources)"> + <ng-template + [ngIf]="element.type !== 'odahu' + && element.type !== 'edge node' + && (element.status==='running' || element.status==='stopped') + && !clustersInProgress(element.resources)" + > <datalab-checkbox [checked]="element.isSelected" (toggleSelection)="toggleActionForAll(element)" - > - </datalab-checkbox> + ></datalab-checkbox> </ng-template> </td> </ng-container> @@ -50,12 +53,18 @@ <ng-container matColumnDef="user"> <th mat-header-cell *matHeaderCellDef class="user label-header"> <span class="label">User</span> - <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> + <button + mat-icon-button + aria-label="More" + class="ar" + (click)="toggleFilterRow()" + > <i class="material-icons"> <span *ngIf="filtering && filterForm.users.length > 0 && !collapsedFilterRow">filter_list</span> <span [hidden]="filtering && filterForm.users.length > 0 && !collapsedFilterRow">more_vert</span> </i> - </button> </th> + </button> + </th> <td mat-cell *matCellDef="let element" class="user-name ellipsis" @@ -70,36 +79,54 @@ <ng-container matColumnDef="project"> <th mat-header-cell *matHeaderCellDef class="project label-header"> <span class="label">Project</span> - <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> + <button + mat-icon-button + aria-label="More" + class="ar" + (click)="toggleFilterRow()" + > <i class="material-icons"> <span *ngIf="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">filter_list</span> <span [hidden]="filtering && filterForm.projects.length > 0 && !collapsedFilterRow">more_vert</span> </i> - </button> </th> + </button> + </th> <td mat-cell *matCellDef="let element">{{ element.project }}</td> </ng-container> <ng-container matColumnDef="endpoint"> <th mat-header-cell *matHeaderCellDef class="endpoint label-header"> <span class="label">Endpoint</span> - <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> + <button + mat-icon-button + aria-label="More" + class="ar" + (click)="toggleFilterRow()" + > <i class="material-icons"> <span *ngIf="filtering && filterForm.endpoints.length > 0 && !collapsedFilterRow">filter_list</span> <span [hidden]="filtering && filterForm.endpoints.length > 0 && !collapsedFilterRow">more_vert</span> </i> - </button> </th> + </button> + </th> <td mat-cell *matCellDef="let element">{{ element.endpoint }}</td> </ng-container> <ng-container matColumnDef="type"> <th mat-header-cell *matHeaderCellDef class="type label-header"> <span class="label">Name</span> - <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> + <button + mat-icon-button + aria-label="More" + class="ar" + (click)="toggleFilterRow()" + > <i class="material-icons"> <span *ngIf="filtering && filterForm.type.length > 0 && !collapsedFilterRow">filter_list</span> <span [hidden]="filtering && filterForm.type.length > 0 && !collapsedFilterRow">more_vert</span> </i> - </button> </th> + </button> + </th> <td type mat-cell *matCellDef="let element"> <span *ngIf="element.name" @@ -115,12 +142,18 @@ <ng-container matColumnDef="shape"> <th mat-header-cell *matHeaderCellDef class="shape label-header"> <span class="label">Shape / Resource id</span> - <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> + <button + mat-icon-button + aria-label="More" + class="ar" + (click)="toggleFilterRow()" + > <i class="material-icons"> <span *ngIf="filtering && filterForm.shapes.length > 0 && !collapsedFilterRow">filter_list</span> <span [hidden]="filtering && filterForm.shapes.length > 0 && !collapsedFilterRow">more_vert</span> </i> - </button> </th> + </button> + </th> <td mat-cell *matCellDef="let element" class="shape"> <div>{{ element.shape || element.ip }}</div> <div *ngIf="element.gpu_type">{{ element.gpu_type }}</div> @@ -131,13 +164,18 @@ <ng-container matColumnDef="status"> <th mat-header-cell *matHeaderCellDef class="status label-header"> <span class="label">Status</span> - - <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> + <button + mat-icon-button + aria-label="More" + class="ar" + (click)="toggleFilterRow()" + > <i class="material-icons"> <span *ngIf="filtering && filterForm.statuses.length > 0 && !collapsedFilterRow">filter_list</span> <span [hidden]="filtering && filterForm.statuses.length > 0 && !collapsedFilterRow">more_vert</span> </i> - </button> </th> + </button> + </th> <td mat-cell *matCellDef="let element" class="ani status" > <span ngClass="{{element.status || ''}}">{{ element.status }}</span> </td> @@ -146,12 +184,18 @@ <ng-container matColumnDef="resources"> <th mat-header-cell *matHeaderCellDef class="resources label-header"> <span class="label">Compute</span> - <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> + <button + mat-icon-button + aria-label="More" + class="ar" + (click)="toggleFilterRow()" + > <i class="material-icons"> <span *ngIf="filtering && filterForm.resources.length > 0 && !collapsedFilterRow">filter_list</span> <span [hidden]="filtering && filterForm.resources.length > 0 && !collapsedFilterRow">more_vert</span> </i> - </button> </th> + </button> + </th> <td mat-cell *matCellDef="let element" class="ani resources"> <div class="source" *ngIf="element.resources"> <div *ngIf="!element.resources?.length"> @@ -160,21 +204,27 @@ <div *ngIf="element.resources?.length"> <div *ngFor="let resource of element.resources" class="resource-wrap"> <div class="resource-name"> - <a> - {{ resource.computational_name }} - </a> + <a>{{ resource.computational_name }}</a> </div> <span ngClass="{{resource.status || ''}}" class="resource-status">{{ resource.status }}</span> <div class="resource-actions"> <span class="not-allow"> <a class="start-stop-action" *ngIf="resource.image === 'docker.datalab-dataengine'"> - <i class="material-icons" (click)="toggleResourceAction(element, 'stop', resource)" - [ngClass]="{'not-allowed' : resource.status !== 'running' || selected?.length }">pause_circle_outline</i> + <i + class="material-icons" + (click)="toggleResourceAction(element, 'stop', resource)" + [ngClass]="{'not-allowed' : resource.status !== 'running' || selected?.length }" + > + pause_circle_outline + </i> </a> </span> <span class="not-allow"> - <a class="remove_butt" (click)="toggleResourceAction(element, 'terminate', resource)" - [ngClass]="{ 'disabled' : element.status !== 'running' || (resource.status !== 'running' && resource.status !== 'stopped') || selected?.length }"> + <a + class="remove_butt" + (click)="toggleResourceAction(element, 'terminate', resource)" + [ngClass]="{ 'disabled' : element.status !== 'running' || (resource.status !== 'running' && resource.status !== 'stopped') || selected?.length }" + > <i class="material-icons">highlight_off</i> </a> </span> @@ -191,30 +241,49 @@ </th> <td mat-cell *matCellDef="let element" class="settings actions-col"> <span [ngClass]="{'not-allow' : selected?.length}"> - <span #settings class="actions" (click)="actions.toggle($event, settings)" *ngIf="element.type !== 'edge node' && element.type !== 'odahu'" + <span + #settings + class="actions" + (click)="actions.toggle($event, settings)" + *ngIf="element.type !== 'edge node' && element.type !== 'odahu'" [ngClass]="{ 'disabled' : (element.status !== 'running' && element.status !== 'stopped') - || selected?.length || inProgress(element.resources)}"> - + || selected?.length || inProgress(element.resources)}" + > </span> </span> <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left"> <ul class="list-unstyled"> <li - matTooltip="{{ element.type !== 'edge node' ? 'Unable to stop notebook because at least one computational resource is in progress' : 'Unable to stop edge node because at least one resource of this user is in progress' }}" - matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(element)" - [hidden]="element.name === 'edge node' && element.status === 'stopped'"> - <div (click)="toggleResourceAction(element, 'stop')" - [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' || element.status === 'starting' || element.status === 'creating image' || element.status === 'failed' || isResourcesInProgress(element)}"> + matTooltip="{{ element.type !== 'edge node' + ? 'Unable to stop notebook because at least one computational resource is in progress' + : 'Unable to stop edge node because at least one resource of this user is in progress' }}" + matTooltipPosition="above" + [matTooltipDisabled]="!isResourcesInProgress(element)" + [hidden]="element.name === 'edge node' && element.status === 'stopped'" + > + <div + (click)="toggleResourceAction(element, 'stop')" + [ngClass]="{'not-allowed' : element.status === 'stopped' || element.status === 'stopping' || + element.status === 'starting' || element.status === 'creating image' || + element.status === 'failed' || isResourcesInProgress(element)}" + > <i class="material-icons">pause_circle_outline</i> <span>Stop</span> </div> </li> - <li *ngIf="element.name !== 'edge node'" + <li + *ngIf="element.name !== 'edge node'" matTooltip="Unable to terminate notebook because at least one compute is in progress" - matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(element)"> - <div (click)="toggleResourceAction(element, 'terminate')" - [ngClass]="{'not-allowed' : element.status !== 'running' && element.status !== 'stopped' || isResourcesInProgress(element)}"> + matTooltipPosition="above" + [matTooltipDisabled]="!isResourcesInProgress(element)" + > + <div + (click)="toggleResourceAction(element, 'terminate')" + [ngClass]="{'not-allowed' : element.status !== 'running' && + element.status !== 'stopped' || + isResourcesInProgress(element)}" + > <i class="material-icons">phonelink_off</i> <span>Terminate</span> </div> @@ -234,113 +303,186 @@ <!-- FILTERING --> <ng-container matColumnDef="checkbox-filter" sticky> - <th mat-header-cell *matHeaderCellDef class="filter-row-item"> - - </th> + <th mat-header-cell *matHeaderCellDef class="filter-row-item"></th> </ng-container> <ng-container matColumnDef="user-filter" sticky> <th mat-header-cell *matHeaderCellDef class="filter-row-item"> - <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="'users'" [items]="filterConfiguration.users" - [model]="filterForm.users"></multi-select-dropdown> + <multi-select-dropdown + (selectionChange)="onUpdate($event)" + [type]="'users'" + [items]="filterConfiguration.users" + [model]="filterForm.users" + ></multi-select-dropdown> </th> </ng-container> + <ng-container matColumnDef="type-filter" sticky> <th mat-header-cell *matHeaderCellDef class="filter-row-item"> - <input placeholder="Filter by name" type="text" class="form-control filter-field" - [value]="filterForm.type" (input)="onFilterNameUpdate($event.target['value'])"/> + <input + placeholder="Filter by name" + type="text" + class="form-control filter-field" + [value]="filterForm.type" + (input)="onFilterNameUpdate($event.target['value'])" + /> </th> </ng-container> + <ng-container matColumnDef="project-filter" sticky> <th mat-header-cell *matHeaderCellDef class="filter-row-item"> - <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="'projects'" - [items]="filterConfiguration.projects" [model]="filterForm.projects"></multi-select-dropdown> + <multi-select-dropdown + (selectionChange)="onUpdate($event)" + [type]="'projects'" + [items]="filterConfiguration.projects" + [model]="filterForm.projects" + ></multi-select-dropdown> </th> </ng-container> <ng-container matColumnDef="endpoint-filter" sticky> <th mat-header-cell *matHeaderCellDef class="filter-row-item"> - <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="'endpoints'" - [items]="filterConfiguration.endpoints" [model]="filterForm.endpoints"></multi-select-dropdown> + <multi-select-dropdown + (selectionChange)="onUpdate($event)" + [type]="'endpoints'" + [items]="filterConfiguration.endpoints" + [model]="filterForm.endpoints" + ></multi-select-dropdown> </th> </ng-container> + <ng-container matColumnDef="shape-filter" sticky> <th mat-header-cell *matHeaderCellDef class="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="status-filter" sticky> <th mat-header-cell *matHeaderCellDef class="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="resource-filter" sticky> <th mat-header-cell *matHeaderCellDef class="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="actions-filter" sticky> <th mat-header-cell *matHeaderCellDef class="actions-col filter-row-item"> <div class="actions"> - <button mat-icon-button class="btn reset" (click)="resetFilterConfigurations()" [disabled]="!isFilterSelected"> + <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)="applyFilter(filterForm)" - [disabled]="isFilterChanged"> - <i class="material-icons" - [ngClass]="{'not-allowed': allFilteredEnvironmentData?.length == 0 && !filtering}">done</i> + <button + mat-icon-button + class="btn apply" + (click)="applyFilter(filterForm)" + [disabled]="isFilterChanged" + > + <i + class="material-icons" + [ngClass]="{'not-allowed': allFilteredEnvironmentData?.length == 0 && !filtering}" + > + done + </i> </button> </div> </th> </ng-container> - <ng-container matColumnDef="placeholder"> <td mat-footer-cell *matFooterCellDef colspan="8"> - <div class="info" - *ngIf="(!allFilteredEnvironmentData) && !filtering || (allFilteredEnvironmentData?.length == 0) && !filtering"> - To start working, please, create new environment</div> + <div + class="info" + *ngIf="(!allFilteredEnvironmentData) && !filtering || (allFilteredEnvironmentData?.length == 0) && !filtering" + > + To start working, please, create new environment + </div> <div *ngIf="(allFilteredEnvironmentData?.length == 0) && filtering" class="info">No matches found</div> </td> </ng-container> <ng-container matColumnDef="scrollButtons"> - <td mat-footer-cell *matFooterCellDef colspan="9" class="buttons-wrap"> - <div class="buttons" [ngStyle]="{'width.px': tableWrapperWidth }" [hidden]="!tableWrapperWidth"> - <div class="button-container"> - <button mat-mini-fab aria-label="Scroll left" - (click)="sctollTo('left')" - [ngClass]="{'not-allowed': wrapper.scrollLeft === 0 }" - > - <mat-icon [ngClass]="{'highlight': wrapper.scrollLeft !== 0 }">keyboard_arrow_left</mat-icon> - </button> - </div> - <div class="button-container" [ngClass]="{'not-allowed': checkMaxRight()}"> - <button mat-mini-fab aria-label="Scroll right" - (click)="sctollTo('right')" - - [ngClass]="{'not-allowed': !(isMaxRight | async)}" - > - <mat-icon [ngClass]="{'highlight': (isMaxRight | async) || false}">keyboard_arrow_right</mat-icon> - </button> - </div> + <td + mat-footer-cell + *matFooterCellDef + colspan="9" + class="buttons-wrap" + > + <div + class="buttons" + [ngStyle]="{'width.px': tableWrapperWidth }" + [hidden]="!tableWrapperWidth" + > + <div class="button-container"> + <button + mat-mini-fab aria-label="Scroll left" + (click)="sctollTo('left')" + [ngClass]="{'not-allowed': wrapper.scrollLeft === 0 }" + > + <mat-icon [ngClass]="{'highlight': wrapper.scrollLeft !== 0 }">keyboard_arrow_left</mat-icon> + </button> </div> - </td> - </ng-container> + <div class="button-container" [ngClass]="{'not-allowed': checkMaxRight()}"> + <button + mat-mini-fab aria-label="Scroll right" + (click)="sctollTo('right')" + [ngClass]="{'not-allowed': !(isMaxRight | async)}" + > + <mat-icon [ngClass]="{'highlight': (isMaxRight | async) || false}">keyboard_arrow_right</mat-icon> + </button> + </div> + </div> + </td> + </ng-container> - <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true" class="header-row"></tr> - <tr [hidden]="!collapsedFilterRow" mat-header-row *matHeaderRowDef="displayedFilterColumns; sticky: true" - class="filter-row"></tr> + <tr + mat-header-row + *matHeaderRowDef="displayedColumns; sticky: true" + class="header-row" + ></tr> + <tr + [hidden]="!collapsedFilterRow" + mat-header-row + *matHeaderRowDef="displayedFilterColumns; sticky: true" + class="filter-row" + ></tr> <tr mat-row *matRowDef="let row; columns: displayedColumns;"></tr> - - <tr [hidden]="allFilteredEnvironmentData?.length" mat-footer-row *matFooterRowDef="['placeholder']" class="info"></tr> - <tr [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length" mat-footer-row *matFooterRowDef="['scrollButtons']; sticky: true" class="info"></tr> + <tr + [hidden]="allFilteredEnvironmentData?.length" + mat-footer-row *matFooterRowDef="['placeholder']" + class="info" + ></tr> + <tr + [hidden]="!tableEl || !tableEl['offsetWidth'] || tableWrapper.offsetWidth - tableEl['offsetWidth'] > -16 || !allFilteredEnvironmentData?.length" + mat-footer-row + *matFooterRowDef="['scrollButtons']; sticky: true" + class="info" + ></tr> </table> - </div> </div> </div> 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 59d5254..888cfbb 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 @@ -17,60 +17,61 @@ * under the License. */ -.managment-wrapper{ +.managment-wrapper { position: relative; height: calc(100vh - 130px); - scroll-behavior: smooth; margin-left: -15px; margin-right: -15px; padding-left: 15px; padding-right: 15px; + scroll-behavior: smooth; overflow: auto; .scroll-wrapper { overflow: auto; scroll-behavior: smooth; - box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), 0px 6px 10px 0px rgba(0, 0, 0, 0.14), 0px 1px 18px 0px rgba(0, 0, 0, 0.12); + box-shadow: 0px 3px 5px -1px rgba(0, 0, 0, 0.2), + 0px 6px 10px 0px rgba(0, 0, 0, 0.14), + 0px 1px 18px 0px rgba(0, 0, 0, 0.12); } - .ani{ + .ani { max-height: calc(100vh - 130px); position: relative; - .wrapper{ - width: 100%; + .wrapper { + position: relative; display: block; + width: 100%; max-height: calc(100vh - 130px); overflow: unset; - position: relative; scroll-behavior: smooth; } } } .data-grid { - &.management { - .filter-row-item{ + .filter-row-item { padding: 5px; } - .settings{ + .settings { min-width: 7%; } - .mat-column-checkbox{ + .mat-column-checkbox { + min-width: 38px; padding-left: 10px; padding-right: 0px; - min-width: 38px; - &.label-header{ - .empty-checkbox{ - z-index: 1011 - } + &.label-header { + .empty-checkbox { + z-index: 1011; + } } } - .user{ + .user { width: 15%; min-width: 180px; } @@ -87,13 +88,13 @@ .shape { width: 15% !important; - .label{ + .label { position: absolute; } min-width: 150px; } - .endpoint{ + .endpoint { width: 10% !important; min-width: 140px; } @@ -115,15 +116,13 @@ .settings { padding-right: 14px; - - } .actions { + position: sticky; margin-top: 0px; padding-right: 10px; - position: sticky; - .label{ + .label { padding-right: 5px; } } @@ -132,39 +131,37 @@ width: 6%; } - .label-header{ + .label-header { padding-left: 15px; height: 56px; - .ar{ - left: 21px; + .ar { + left: 20px; top: 2px; - &.checkbox-border - { - left: 19px; + &.checkbox-border { + left: 18px; } } - .settings{ + .settings { min-width: 7%; } - &.mat-column-checkbox{ + &.mat-column-checkbox { z-index: 12 !important; padding-left: 10px; - .ar{ + .ar { position: absolute; top: 10px; } } - &.user{ + &.user { z-index: 11 !important; - } - &.type{ + &.type { z-index: 10 !important; } @@ -203,37 +200,36 @@ } .dashboard_table_body { - td:first-child { cursor: default; } } - .user-name{ + .user-name { + max-width: 250px; padding-right: 22px; overflow: hidden; word-break: break-all; white-space: nowrap; - max-width: 250px; } - .buttons-wrap{ + .buttons-wrap { padding: 0; - border-top: 1px solid rgba(0,0,0,.12); + border-top: 1px solid rgba(0, 0, 0, 0.12); transform: translateY(-1px); } - .buttons{ + .buttons { position: sticky; left: 0; } - button{ + button { background-color: #fff; box-shadow: none; - mat-icon{ + mat-icon { color: lightgrey; - &.highlight{ + &.highlight { color: #35afd5; } } @@ -258,8 +254,8 @@ table.management { .label { display: inline-block; padding-top: 14px; - vertical-align: super !important; padding-left: 5px; + vertical-align: super !important; } .actions { text-align: right; @@ -273,26 +269,26 @@ table.management { .filter-row { background: inherit; - .filter-row-item{ + .filter-row-item { left: auto !important; - &.actions-col{ - right: -15px; - } + &.actions-col { + right: -15px; + } } - .filter-field{ + .filter-field { font-size: 13px; padding-left: 15px; } } - .info{ + .info { padding: 40px; text-align: center; } .source .resource-wrap { - .resource-actions{ + .resource-actions { padding-right: 0; } .resource-name .detailed-link { @@ -301,24 +297,22 @@ table.management { } } -.computation{ +.computation { cursor: pointer; color: #35afd5; } -.table-footer{ +.table-footer { height: 48px; } -.header-checkbox{ +.header-checkbox { margin-top: 7px; float: left; } - - @media screen and (max-width: 1550px) { - .managment-wrapper{ + .managment-wrapper { .source { .resource-wrap { .resource-name { @@ -331,11 +325,8 @@ table.management { .resource-actions { width: 20%; - } } } } } - - 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 7871156..78ecf28 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 @@ -30,18 +30,17 @@ import { AfterViewChecked, ApplicationRef } from '@angular/core'; +import { ChangeDetectorRef } from '@angular/core'; import { MatDialog, MatDialogRef, MAT_DIALOG_DATA } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; +import { BehaviorSubject, Subject, timer } from 'rxjs'; -import { HealthStatusService } from '../../../core/services'; import { SortUtils } from '../../../core/util'; import { EnvironmentsDataService } from '../management-data.service'; import { EnvironmentModel, ManagementConfigModel } from '../management.model'; -import {ProgressBarService} from '../../../core/services/progress-bar.service'; -import {DetailDialogComponent} from '../../../resources/exploratory/detail-dialog'; -import {BehaviorSubject, Subject, timer} from 'rxjs'; -import { ChangeDetectorRef } from '@angular/core'; -import {CompareUtils} from '../../../core/util/compareUtils'; +import { ProgressBarService } from '../../../core/services/progress-bar.service'; +import { DetailDialogComponent } from '../../../resources/exploratory/detail-dialog'; +import { CompareUtils } from '../../../core/util/compareUtils'; export interface ManageAction { action: string; @@ -93,7 +92,7 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView this.checkMaxRight(); } - displayedColumns: string[] = [ 'checkbox', 'user', 'type', 'project', 'endpoint', 'shape', 'status', 'resources', 'actions']; + displayedColumns: string[] = ['checkbox', 'user', 'type', 'project', 'endpoint', 'shape', 'status', 'resources', 'actions']; displayedFilterColumns: string[] = ['checkbox-filter', 'user-filter', 'type-filter', 'project-filter', 'endpoint-filter', 'shape-filter', 'status-filter', 'resource-filter', 'actions-filter']; public selected; public allActiveNotebooks: any; @@ -102,7 +101,6 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView public isFilterChanged: boolean; constructor( - private healthStatusService: HealthStatusService, private environmentsDataService: EnvironmentsDataService, public toastr: ToastrService, public dialog: MatDialog, @@ -115,7 +113,6 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView this.getEnvironmentData(); } - ngAfterViewInit() { this.progressBarService.startProgressBar(); this.tableEl = this.table._elementRef.nativeElement; @@ -134,16 +131,20 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView getEnvironmentData() { this.progressBarService.startProgressBar(); - this.environmentsDataService._data.subscribe(data => { - if (data) { - this.allEnvironmentData = EnvironmentModel.loadEnvironments(data); - this.getDefaultFilterConfiguration(data); - this.applyFilter(this.cashedFilterForm || this.filterForm); - } - this.progressBarService.stopProgressBar(); - }, () => { - this.progressBarService.stopProgressBar(); - }); + this.environmentsDataService._data + .subscribe( + data => { + if (data) { + this.allEnvironmentData = EnvironmentModel.loadEnvironments(data); + this.getDefaultFilterConfiguration(data); + this.applyFilter(this.cashedFilterForm || this.filterForm); + } + this.progressBarService.stopProgressBar(); + }, + () => { + this.progressBarService.stopProgressBar(); + } + ); } buildGrid(): void { @@ -183,7 +184,9 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView const containsStatus = (list, selectedItems) => { if (list) { - return list.filter((item: any) => { if (selectedItems.indexOf(item.status) !== -1) return item; }); + return list.filter((item: any) => { + if (selectedItems.indexOf(item.status) !== -1) return item; + }); } }; @@ -191,19 +194,31 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView if (config) { 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 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 || - config.shapes.indexOf(item.gpu_type) !== -1 || - config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1 ) : true; - const isProject = config.projects.length > 0 ? (config.projects.indexOf(item.project) !== -1) : true; - const isEndpoint = config.endpoints.length > 0 ? (config.endpoints.indexOf(item.endpoint) !== -1) : true; + 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 || + config.shapes.indexOf(item.gpu_type) !== -1 || + config.shapes.indexOf(`GPU count: ${item.gpu_count}`) !== -1) + : true; + const isProject = config.projects.length > 0 + ? (config.projects.indexOf(item.project) !== -1) + : true; + const isEndpoint = config.endpoints.length > 0 + ? (config.endpoints.indexOf(item.endpoint) !== -1) + : true; const modifiedResources = containsStatus(item.resources, config.resources); - let isResources = config.resources.length > 0 ? (modifiedResources && modifiedResources.length > 0) : true; - if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) { item.resources = modifiedResources; } + let isResources = config.resources.length > 0 + ? (modifiedResources && modifiedResources.length > 0) + : true; + if (config.resources.length > 0 && modifiedResources && modifiedResources.length > 0) { + item.resources = modifiedResources; + } if (config.resources && config.resources.length === 0 && config.type === 'active' || modifiedResources && modifiedResources.length >= 0 && config.resources.length > 0 && config.type === 'active') { @@ -217,8 +232,8 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView this.allFilteredEnvironmentData = filteredData; this.allActiveNotebooks = this.allFilteredEnvironmentData .filter(v => v.name && - (v.status === 'running' || v.status === 'stopped') && - !this.clustersInProgress(v.resources || [])); + (v.status === 'running' || v.status === 'stopped') && + !this.clustersInProgress(v.resources || [])); this.checkFilters(); } @@ -252,8 +267,8 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView } private getDefaultFilterConfiguration(data): void { - const users = [], projects = [], shapes = [], statuses = [], - resources = [], endpoints = [], gpuTypes = [], gpuCounts = []; + const users = [], projects = [], shapes = [], statuses = [], + resources = [], endpoints = [], gpuTypes = [], gpuCounts = []; data && data.forEach((item: any) => { if (item.user && users.indexOf(item.user) === -1) users.push(item.user); @@ -264,10 +279,10 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView if (item.gpu_type && gpuTypes.indexOf(item.gpu_type) === -1) gpuTypes.push(item.gpu_type); if (item.gpu_count && gpuCounts.indexOf(`GPU count: ${item.gpu_count}`) === -1) gpuCounts.push(`GPU count: ${item.gpu_count}`); if (item.computational_resources) { - item.computational_resources.map((resource: any) => { - if (resources.indexOf(resource.status) === -1) resources.push(resource.status); - resources.sort(SortUtils.statusSort); - }); + item.computational_resources.map((resource: any) => { + if (resources.indexOf(resource.status) === -1) resources.push(resource.status); + resources.sort(SortUtils.statusSort); + }); } }); @@ -278,8 +293,9 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView if (!data.exploratory_urls || !data.exploratory_urls.length) { return; } - this.dialog.open(DetailDialogComponent, { data: - {notebook: data, buckets: [], type: 'environment'}, + this.dialog.open(DetailDialogComponent, { + data: + { notebook: data, buckets: [], type: 'environment' }, panelClass: 'modal-lg' }) .afterClosed().subscribe(() => {}); @@ -321,12 +337,12 @@ export class ManagementGridComponent implements OnInit, AfterViewInit, AfterView public checkMaxRight() { let arg; - if (this.wrapper && this.table) { - arg = this.wrapper.nativeElement.offsetWidth + - this.wrapper.nativeElement.scrollLeft + 2 <= this.table._elementRef.nativeElement.offsetWidth; - } - - return this.isMaxRight.next(arg); + if (this.wrapper && this.table) { + arg = this.wrapper.nativeElement.offsetWidth + + this.wrapper.nativeElement.scrollLeft + 2 <= this.table._elementRef.nativeElement.offsetWidth; + } + + return this.isMaxRight.next(arg); } } @@ -436,7 +452,7 @@ export class ReconfirmationDialogComponent { if (notebook.resources.length) { this.isClusterLength = true; } - return notebook; + 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 240a9e0..4a4cf55 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 @@ -23,7 +23,8 @@ <div class="action-select-wrapper admin-group" > <span class="action-button-wrapper"> <button - type="button" class="butt actions-btn" + type="button" + class="butt actions-btn" mat-raised-button [disabled]="!selected.length" (click)="toogleActions();$event.stopPropagation()" @@ -34,18 +35,20 @@ </span> <div class="action-menu" *ngIf="isActionsOpen"> <span> + <button + type="button" + class="butt action-menu-item" + [ngClass]="{'disabled': selectedRunning.length === 0 || selectedStopped.length !== 0 }" + mat-raised-button + [disabled]="selectedRunning.length === 0" + (click)="resourseAction('stop');$event.stopPropagation()" + > + Stop + </button> + </span> <button - type="button" class="butt action-menu-item" - [ngClass]="{'disabled': selectedRunning.length === 0 || selectedStopped.length !== 0 }" - mat-raised-button - [disabled]="selectedRunning.length === 0" - (click)="resourseAction('stop');$event.stopPropagation()" - > - Stop - </button> - </span> - <button - type="button" class="butt action-menu-item" + type="button" + class="butt action-menu-item" mat-raised-button (click)="resourseAction('terminate');$event.stopPropagation()" > @@ -53,26 +56,42 @@ </button> </div> </div> - <button mat-raised-button class="butt ssn" (click)="showEndpointsDialog()"> + <button + mat-raised-button + class="butt ssn" + (click)="showEndpointsDialog()" + > <i class="material-icons"></i>Endpoints </button> <!-- <button mat-raised-button class="butt ssn" (click)="openSsnMonitorDialog()"> <i class="material-icons"></i>SSN Monitor </button> --> - <button mat-raised-button class="butt env" (click)="openManageEnvironmentDialog()"> - Manage DataLab quotas + <button + mat-raised-button + class="butt env" + (click)="openManageEnvironmentDialog()" + > + Manage DataLab quotas </button> <!-- <button mat-raised-button class="butt" (click)="showBackupDialog()" [disabled]="creatingBackup"> <i class="material-icons">backup</i>Backup </button> --> </div> - <button mat-raised-button class="butt" (click)="refreshGrid()"> + <button + mat-raised-button + class="butt" + (click)="refreshGrid()" + > <i class="material-icons refresh-icon">autorenew</i>Refresh </button> </div> <mat-divider></mat-divider> - <management-grid [currentUser]="user.toLowerCase()" [isAdmin]="healthStatus?.admin" - [environmentsHealthStatuses]="healthStatus?.list_resources" (refreshGrid)="buildGrid()" - (actionToggle)="toggleResourceAction($event)" (emitSelectedList)="selectedList($event)"> - </management-grid> + <management-grid + [currentUser]="user.toLowerCase()" + [isAdmin]="healthStatus?.admin" + [environmentsHealthStatuses]="healthStatus?.list_resources" + (refreshGrid)="buildGrid()" + (actionToggle)="toggleResourceAction($event)" + (emitSelectedList)="selectedList($event)" + ></management-grid> </div> 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 38779fc..9ce01ad 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, ViewChild} from '@angular/core'; +import { Component, OnInit, ViewChild } from '@angular/core'; import { MatDialog } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; @@ -39,9 +39,9 @@ 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 {ManagementGridComponent, ReconfirmationDialogComponent} from './management-grid/management-grid.component'; -import {FolderTreeComponent} from '../../resources/bucket-browser/folder-tree/folder-tree.component'; +import { ConfirmationDialogComponent, ConfirmationDialogType } from '../../shared/modal-dialog/confirmation-dialog'; +import { ManagementGridComponent, ReconfirmationDialogComponent } from './management-grid/management-grid.component'; +import { FolderTreeComponent } from '../../resources/bucket-browser/folder-tree/folder-tree.component'; @Component({ selector: 'environments-management', @@ -58,8 +58,7 @@ export class ManagementComponent implements OnInit { public selectedRunning: any[]; public selectedStopped: any[]; - @ViewChild(ManagementGridComponent, {static: true}) managementGrid; - + @ViewChild(ManagementGridComponent, { static: true }) managementGrid; constructor( public toastr: ToastrService, @@ -84,7 +83,7 @@ export class ManagementComponent implements OnInit { } public refreshGrid() { - this.buildGrid(); + this.buildGrid(); } public manageEnvironmentAction($event) { @@ -95,9 +94,11 @@ export class ManagementComponent implements OnInit { $event.environment.project, $event.environment.type === 'edge node' ? 'edge' : $event.environment.name, $event.resource ? $event.resource.computational_name : null - ).subscribe( + ) + .subscribe( () => this.buildGrid(), - error => this.toastr.error('Environment management failed!', 'Oops!')); + error => this.toastr.error('Environment management failed!', 'Oops!') + ); } // showBackupDialog() { @@ -111,10 +112,14 @@ export class ManagementComponent implements OnInit { openManageEnvironmentDialog() { this.projectService.getProjectsList().subscribe(projectsList => { - this.getTotalBudgetData().subscribe(total => { - this.dialogRef = this.dialog.open(ManageEnvironmentComponent, { data: { projectsList, total }, panelClass: 'modal-xl-s' }); - this.dialogRef.afterClosed().subscribe(result => result && this.setBudgetLimits(result)); - }, () => this.toastr.error('Failed users list loading!', 'Oops!')); + this.getTotalBudgetData() + .subscribe( + total => { + this.dialogRef = this.dialog.open(ManageEnvironmentComponent, { data: { projectsList, total }, panelClass: 'modal-xl-s' }); + this.dialogRef.afterClosed().subscribe(result => result && this.setBudgetLimits(result)); + }, + () => this.toastr.error('Failed users list loading!', 'Oops!') + ); }); } @@ -131,28 +136,31 @@ export class ManagementComponent implements OnInit { setBudgetLimits($event) { if ($event.projects.length) { - this.projectService.updateProjectsBudget($event.projects).subscribe((result: any) => { - if ($event.isTotalChanged) { - this.healthStatusService.updateTotalBudgetData($event.total).subscribe((res: any) => { - result.status === HTTP_STATUS_CODES.OK - && res.status === HTTP_STATUS_CODES.NO_CONTENT + this.projectService.updateProjectsBudget($event.projects) + .subscribe( + (result: any) => { + if ($event.isTotalChanged) { + this.healthStatusService.updateTotalBudgetData($event.total).subscribe((res: any) => { + result.status === HTTP_STATUS_CODES.OK + && res.status === HTTP_STATUS_CODES.NO_CONTENT + && this.toastr.success('Budget limits updated!', 'Success!'); + this.buildGrid(); + }); + } else { + result.status === HTTP_STATUS_CODES.OK && this.toastr.success('Budget limits updated!', 'Success!'); + this.buildGrid(); + } + }, + error => this.toastr.error(error.message, 'Oops!')); + } else { + this.healthStatusService.updateTotalBudgetData($event.total) + .subscribe((res: any) => { + res.status === HTTP_STATUS_CODES.NO_CONTENT && this.toastr.success('Budget limits updated!', 'Success!'); - this.buildGrid(); - }); - } else { - result.status === HTTP_STATUS_CODES.OK && this.toastr.success('Budget limits updated!', 'Success!'); this.buildGrid(); - } - - }, error => this.toastr.error(error.message, 'Oops!')); - } else { - this.healthStatusService.updateTotalBudgetData($event.total).subscribe((res: any) => { - res.status === HTTP_STATUS_CODES.NO_CONTENT - && this.toastr.success('Budget limits updated!', 'Success!'); - this.buildGrid(); - }); - } + }); } + } // manageEnvironment(event: { action: string, project: any }) { @@ -216,12 +224,18 @@ export class ManagementComponent implements OnInit { } toggleResourceAction($event): void { - const {environment, action, resource} = $event; + const { environment, action, resource } = $event; if (resource) { const resource_name = resource ? resource.computational_name : environment.name; this.dialog.open(ReconfirmationDialogComponent, { - data: { action, resource_name, user: environment.user, type: 'cluster'}, - width: '550px', panelClass: 'error-modalbox' + data: { + action, + resource_name, + user: environment.user, + type: 'cluster' + }, + width: '550px', + panelClass: 'error-modalbox' }).afterClosed().subscribe(result => { result && this.manageEnvironmentAction({ action, environment, resource }); }); @@ -230,15 +244,20 @@ export class ManagementComponent implements OnInit { if (action === 'stop') { notebooks = notebooks.filter(note => note.status !== 'stopped'); this.dialog.open(ReconfirmationDialogComponent, { - data: { notebooks: notebooks, type: 'notebook', action }, - width: '550px', panelClass: 'error-modalbox' + 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 => { - this.buildGrid(); - }, + this.buildGrid(); + }, error => console.log(error) ); }); @@ -250,7 +269,13 @@ export class ManagementComponent implements OnInit { }); } else if (action === 'terminate') { this.dialog.open(ReconfirmationDialogComponent, { - data: { notebooks: notebooks, type: 'notebook', action }, width: '550px', panelClass: 'error-modalbox' + data: { + notebooks: notebooks, + type: 'notebook', + action + }, + width: '550px', + panelClass: 'error-modalbox' }).afterClosed().subscribe((res) => { if (res) { notebooks.forEach((env) => { @@ -268,16 +293,16 @@ export class ManagementComponent implements OnInit { } this.isActionsOpen = false; }); - // } 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!')); + // } 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!')); } } } @@ -291,8 +316,7 @@ export class ManagementComponent implements OnInit { } } - public resourseAction(action) { - this.toggleResourceAction({environment: this.selected, action: action}); + this.toggleResourceAction({ environment: this.selected, action: action }); } } diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts index 7e9dda2..2e67871 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/management.model.ts @@ -102,7 +102,6 @@ export class ManagementConfigModel { public statuses: Array<string>, public resources: Array<string>, public endpoints: Array<string>, - ) { } defaultConfigurations(): void { diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.html b/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.html index 30c818d..a1b2e9f 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.html +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.html @@ -107,7 +107,14 @@ </mat-tab> </mat-tab-group> <div class="text-center"> - <button type="button" class="butt" mat-raised-button (click)="dialogRef.close()">Close</button> + <button + type="button" + class="butt" + mat-raised-button + (click)="dialogRef.close()" + > + Close + </button> </div> </div> <div class="info message" *ngIf="isEmpty(data)"> diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.scss b/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.scss index 9881165..107908c 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.scss +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.scss @@ -19,8 +19,8 @@ .ssn-monitor-dialog { .content-box { - padding-top: 10px !important; position: relative; + padding-top: 10px !important; } .ssn-info { min-height: 400px; @@ -39,13 +39,13 @@ .mat-list-item-content { display: flex; justify-content: initial; - color: #577289; padding: 15px 5px; - border-bottom: 1px solid #f3f2f2; + color: #577289; font-size: 15px; + border-bottom: 1px solid #f3f2f2; .col { width: 50%; font-weight: 300; - } + } } } diff --git a/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.ts b/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.ts index 1ef7634..5aedc1f 100644 --- a/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.ts +++ b/services/self-service/src/main/resources/webapp/src/app/administration/management/ssn-monitor/ssn-monitor.component.ts @@ -20,6 +20,7 @@ import { Component, OnInit, ViewEncapsulation } from '@angular/core'; import { MatDialogRef } from '@angular/material/dialog'; import { ToastrService } from 'ngx-toastr'; + import { DICTIONARY } from '../../../../dictionary/global.dictionary'; import { HealthStatusService } from '../../../core/services'; @@ -42,9 +43,11 @@ export class SsnMonitorComponent implements OnInit { ) { } ngOnInit() { - this.healthStatusService.getSsnMonitorData().subscribe( + this.healthStatusService.getSsnMonitorData() + .subscribe( monitorData => this.data = monitorData, - () => this.toastr.error('Failed ssn data loading!', 'Oops!')); + () => this.toastr.error('Failed ssn data loading!', 'Oops!') + ); } public isEmpty(obj) { --------------------------------------------------------------------- To unsubscribe, e-mail: [email protected] For additional commands, e-mail: [email protected]
