| <!-- |
| ~ Licensed to the Apache Software Foundation (ASF) under one |
| ~ or more contributor license agreements. See the NOTICE file |
| ~ distributed with this work for additional information |
| ~ regarding copyright ownership. The ASF licenses this file |
| ~ to you under the Apache License, Version 2.0 (the |
| ~ "License"); you may not use this file except in compliance |
| ~ with the License. You may obtain a copy of the License at |
| ~ |
| ~ http://www.apache.org/licenses/LICENSE-2.0 |
| ~ |
| ~ Unless required by applicable law or agreed to in writing, |
| ~ software distributed under the License is distributed on an |
| ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| ~ KIND, either express or implied. See the License for the |
| ~ specific language governing permissions and limitations |
| ~ under the License. |
| --> |
| |
| <section class="table-wrapper"> |
| <table mat-table [dataSource]="filteredEnvironments" multiTemplateDataRows |
| class="data-grid resources mat-elevation-z6"> |
| |
| <ng-container matColumnDef="project"> |
| <td mat-cell *matCellDef="let element" [attr.colspan]="8" class="highlight"> {{ element.project }} </td> |
| </ng-container> |
| |
| <!-- <ng-container *ngFor="let column of filteringColumns; let i = index" matColumnDef="{{ column.name }}" |
| [attr.sticky]="column.name === 'name' ? true : null"> |
| <th mat-header-cell *matHeaderCellDef ngClass="{{ column.class }}"> |
| <span class="label">{{ column.title }}</span> |
| <button mat-icon-button *ngIf="column.filtering" aria-label="More" class="ar" (click)="toggleFilterRow()"> |
| <i class="material-icons"> |
| <span *ngIf="filtering && filterForm[column.name].length > 0 && !collapseFilterRow">filter_list</span> |
| <span [hidden]="filtering && filterForm[column.name].length > 0 && !collapseFilterRow">more_vert</span> |
| </i> |
| </button> |
| </th> |
| </ng-container> --> |
| |
| <ng-container matColumnDef="name" sticky> |
| <th mat-header-cell *matHeaderCellDef class="name-col"> |
| <span class="label">Environment name</span> |
| <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> |
| <i class="material-icons"> |
| <span *ngIf="filtering && filterForm.name.length > 0 && !collapseFilterRow">filter_list</span> |
| <span [hidden]="filtering && filterForm.name.length > 0 && !collapseFilterRow">more_vert</span> |
| </i> |
| </button> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="statuses"> |
| <th mat-header-cell *matHeaderCellDef class="status-col"> |
| <span class="label"> Status </span> |
| <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> |
| <i class="material-icons"> |
| <span *ngIf="filtering && filterForm.statuses.length > 0 && !collapseFilterRow">filter_list</span> |
| <span [hidden]="filtering && filterForm.statuses.length > 0 && !collapseFilterRow">more_vert</span> |
| </i> |
| </button> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="shapes"> |
| <th mat-header-cell *matHeaderCellDef class="shape-col"> |
| <span class="label"> Size </span> |
| <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> |
| <i class="material-icons"> |
| <span *ngIf="filtering && filterForm.shapes.length > 0 && !collapseFilterRow">filter_list</span> |
| <span [hidden]="filtering && filterForm.shapes.length > 0 && !collapseFilterRow">more_vert</span> |
| </i> |
| </button> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="tag"> |
| <th mat-header-cell *matHeaderCellDef class="tag-col"> |
| <span class="label"> Tags </span> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="resources"> |
| <th mat-header-cell *matHeaderCellDef class="resources-col"> |
| <span class="label"> Computational resources </span> |
| <button mat-icon-button aria-label="More" class="ar" (click)="toggleFilterRow()"> |
| <i class="material-icons"> |
| <span *ngIf="filtering && filterForm.resources.length > 0 && !collapseFilterRow">filter_list</span> |
| <span [hidden]="filtering && filterForm.resources.length > 0 && !collapseFilterRow">more_vert</span> |
| </i> |
| </button> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="cost"> |
| <th mat-header-cell *matHeaderCellDef class="cost-col"> |
| <span class="label"> Cost </span> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="actions" stickyEnd> |
| <th mat-header-cell *matHeaderCellDef class="actions-col"> |
| <span class="label"> Actions </span> |
| </th> |
| </ng-container> |
| |
| <!-- ----------------------------------------------------- --> |
| |
| <ng-container matColumnDef="expandedDetail"> |
| <td mat-cell *matCellDef="let element" class="exploratory" [attr.colspan]="8" |
| [@detailExpand]="element == expandedElement ? 'expanded' : 'collapsed'" sticky> |
| |
| <tr *ngFor="let element of element.exploratory; let i = index" class="element-row mat-row"> |
| <td class="name-col" (click)="printDetailEnvironmentModal(element)"> |
| <span matTooltip="{{ element.name }}" matTooltipPosition="above">{{ element.name }}</span> |
| </td> |
| <td class="status-col status" ngClass="{{ element.status.toLowerCase() || ''}}"> |
| {{element.status | underscoreless }} |
| </td> |
| <td class="shape-col">{{ element.shape }}</td> |
| |
| <td class="tag-col selection"> |
| <mat-chip-list aria-label="Tags"> |
| <mat-chip matTooltip="Endpoint tag: {{ element.tags.endpoint_tag }}" matTooltipPosition="above"> |
| {{ element.tags.endpoint_tag }} |
| </mat-chip> |
| <mat-chip *ngIf="element.tags.custom_tag" matTooltip="Custom tag: {{ element.tags.custom_tag }}" |
| matTooltipPosition="above"> |
| {{ element.tags.custom_tag }} |
| </mat-chip> |
| <mat-chip matTooltip="User tag: {{ element.tags.user_tag }}" matTooltipPosition="above"> |
| {{ element.tags.user_tag }} |
| </mat-chip> |
| <mat-chip matTooltip="Project tag: {{ element.tags.project_tag }}" matTooltipPosition="above"> |
| {{ element.tags.project_tag }} |
| </mat-chip> |
| </mat-chip-list> |
| </td> |
| |
| <td class="resources-col"> |
| <computational-resources-list [resources]="element.resources" [environment]="element" |
| (buildGrid)="buildGrid($event)"> |
| </computational-resources-list> |
| </td> |
| <td *ngIf="healthStatus?.billingEnabled" class="cost-col"> |
| <span class="total_cost">{{ element.cost || 'N/A' }} {{ element.currency_code || '' }}</span> |
| <span (click)="element.billing && printCostDetails(element)" class="currency_details" |
| [ngClass]="{ 'not-allowed' : !element.billing }"> |
| <i class="material-icons">help_outline</i> |
| </span> |
| </td> |
| |
| <td class="settings"> |
| <span #settings (click)="actions.toggle($event, settings)" class="actions" |
| [ngClass]="{ 'disabled': element.status.toLowerCase() === 'creating' |
| || (element.image === 'docker.dlab-superset' && element.status !== 'running' && element.status !== 'stopped') |
| || (element.image === 'docker.dlab-jupyterlab' && element.status !== 'running' && element.status !== 'stopped') }"> |
| </span> |
| |
| <bubble-up #actions class="list-menu" position="bottom-left" alternative="top-left"> |
| <ul class="list-unstyled"> |
| <div class="active-items" *ngIf="element.status.toLowerCase() !== 'failed' |
| && element.status !== 'terminating' |
| && element.status !== 'terminated' |
| && element.status !== 'creating image'"> |
| <li |
| *ngIf="element.status !== 'stopped' && element.status !== 'stopping' && element.status !== 'starting' && element.status !== 'creating image' && element.status.toLowerCase() !== 'reconfiguring'" |
| matTooltip="Unable to stop notebook because at least one computational resource is in progress" |
| matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(element)"> |
| <div (click)="exploratoryAction(element, 'stop')" |
| [ngClass]="{'not-allowed': isResourcesInProgress(element) }"> |
| <i class="material-icons">pause_circle_outline</i> |
| <span>Stop</span> |
| </div> |
| </li> |
| <li *ngIf="element.status.toLowerCase() === 'stopped' || element.status.toLowerCase() === 'stopping'" |
| matTooltip="{{isEdgeNodeStopped(element) ? 'Unable to run notebook if edge node is stopped.' : 'Unable to run notebook until it will be stopped.'}}" matTooltipPosition="above" |
| [matTooltipDisabled]="!isResourcesInProgress(element) && element.status.toLowerCase() !== 'stopping' && !isEdgeNodeStopped(element)"> |
| <div (click)="exploratoryAction(element, 'run')" |
| [ngClass]="{'not-allowed': isResourcesInProgress(element) || element.status.toLowerCase() === 'stopping' || isEdgeNodeStopped(element) }"> |
| <i class="material-icons">play_circle_outline</i> |
| <span>Run</span> |
| </div> |
| </li> |
| <li *ngIf="element.status.toLowerCase() === 'running' || element.status.toLowerCase() === 'stopped'" |
| matTooltip="Unable to terminate notebook because at least one computational resource is in progress" |
| matTooltipPosition="above" [matTooltipDisabled]="!isResourcesInProgress(element)"> |
| <div (click)="exploratoryAction(element, 'terminate')" |
| [ngClass]="{'not-allowed': isResourcesInProgress(element) }"> |
| <i class="material-icons">phonelink_off</i> |
| <span>Terminate</span> |
| </div> |
| </li> |
| <li (click)="exploratoryAction(element, 'deploy')" |
| *ngIf="element.status === 'running' && element.image !== 'docker.dlab-superset' && element.image !== 'docker.dlab-jupyterlab'"> |
| <i class="material-icons">memory</i> |
| <span>Add compute</span> |
| </li> |
| <li (click)="exploratoryAction(element, 'schedule')" *ngIf="element.status.toLowerCase() === 'running' |
| || element.status.toLowerCase() === 'stopped'"> |
| <i class="material-icons">schedule</i> |
| <span>Scheduler</span> |
| </li> |
| </div> |
| <li (click)="exploratoryAction(element, 'ami')" |
| *ngIf="element.status === 'running' && element.cloud_provider !== 'gcp' && element.image !== 'docker.dlab-superset' && element.image !== 'docker.dlab-jupyterlab'"> |
| <i class="material-icons">view_module</i> |
| <span>Create {{ DICTIONARY[element.cloud_provider].image }}</span> |
| </li> |
| <li (click)="exploratoryAction(element, 'install')" |
| *ngIf="element.image !== 'docker.dlab-superset' && element.image !== 'docker.dlab-jupyterlab'"> |
| <i class="material-icons">developer_board</i> |
| <span>Manage libraries</span> |
| </li> |
| <li *ngIf="element.status === 'running'"> |
| <a target="_blank" [attr.href]="'/#/terminal/' + element.private_ip + '/' + element.endpoint" |
| class="navigate"> |
| <i class="material-icons">laptop</i> |
| <span>Open terminal</span> |
| </a> |
| </li> |
| </ul> |
| </bubble-up> |
| </td> |
| </tr> |
| </td> |
| </ng-container> |
| |
| <!-- FILTER START --> |
| <ng-container matColumnDef="name-filter" sticky> |
| <th mat-header-cell *matHeaderCellDef class="name-col"> |
| <input placeholder="Filter by environment name" type="text" class="form-control filter-field" |
| [value]="filterForm.name" (input)="filterForm.name = $event.target.value" /> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="status-filter"> |
| <th mat-header-cell *matHeaderCellDef class="status-col"> |
| <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="'statuses'" |
| [items]="filterConfiguration.statuses" [model]="filterForm.statuses"></multi-select-dropdown> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="shape-filter"> |
| <th mat-header-cell *matHeaderCellDef class="shape-col"> |
| <multi-select-dropdown (selectionChange)="onUpdate($event)" |
| [type]="'sizes'" [items]="filterConfiguration.shapes" |
| [model]="filterForm.shapes"></multi-select-dropdown> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="tag-filter"> |
| <th mat-header-cell *matHeaderCellDef class="tag-col"> |
| |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="resource-filter"> |
| <th mat-header-cell *matHeaderCellDef class="resources-col"> |
| <multi-select-dropdown (selectionChange)="onUpdate($event)" [type]="'resources'" |
| [items]="filterConfiguration.resources" [model]="filterForm.resources"></multi-select-dropdown> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="cost-filter"> |
| <th mat-header-cell *matHeaderCellDef class="cost-col"> |
| |
| </th> |
| </ng-container> |
| |
| <ng-container matColumnDef="action-filter" stickyEnd> |
| <th mat-header-cell *matHeaderCellDef> |
| <div class="actions"> |
| <button mat-icon-button class="btn reset" (click)="resetFilterConfigurations()" [disabled]="filteredEnvironments.length == 0 && !filtering"> |
| <i class="material-icons">close</i> |
| </button> |
| |
| <button mat-icon-button class="btn apply" (click)="applyFilter_btnClick(filterForm)" |
| [disabled]="filteredEnvironments.length == 0 && !filtering"> |
| <i class="material-icons">done</i> |
| </button> |
| </div> |
| </th> |
| </ng-container> |
| <ng-container matColumnDef="placeholder"> |
| <td mat-footer-cell *matFooterCellDef |
| [colSpan]="!healthStatus?.billingEnabled ? displayedFilterColumns.length -1 : displayedFilterColumns.length" |
| class="info"> |
| <span *ngIf="(!filteredEnvironments) && !filtering || (filteredEnvironments.length == 0) && !filtering"> |
| To start working, please, create new environment</span> |
| <span *ngIf="(filteredEnvironments.length == 0) && filtering">No matches found</span> |
| </td> |
| </ng-container> |
| |
| <!-- FILTER END --> |
| |
| <tr mat-header-row *matHeaderRowDef="displayedColumns; sticky: true" class="header-row"></tr> |
| |
| <tr [hidden]="!collapseFilterRow" mat-header-row *matHeaderRowDef="displayedFilterColumns; sticky: true" |
| class="filter-row"></tr> |
| |
| <tr mat-row *matRowDef="let element; columns: ['project']" class="element-row" |
| [class.expanded-row]="expandedElement === element" |
| (click)="expandedElement = expandedElement === element ? null : element"> |
| </tr> |
| <tr mat-row *matRowDef="let row; columns: ['expandedDetail']" class="detail-row"></tr> |
| |
| <tr [hidden]="filteredEnvironments?.length" mat-footer-row *matFooterRowDef="['placeholder']"></tr> |
| </table> |
| </section> |