release 1.8

This commit is contained in:
Shay Halsband
2022-11-28 18:08:00 +02:00
parent 94308aaa45
commit 9fd8239d1f
219 changed files with 5267 additions and 5493 deletions

7649
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -1,6 +1,6 @@
{
"name": "clearml-webapp",
"version": "1.7.0",
"version": "1.8.0",
"license": "",
"scripts": {
"ng": "ng",
@@ -17,86 +17,87 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^14.1.0",
"@angular/cdk": "^14.1.0",
"@angular/common": "^14.1.0",
"@angular/compiler": "^14.1.0",
"@angular/core": "^14.1.0",
"@angular/forms": "^14.1.0",
"@angular/material": "^14.1.0",
"@angular/platform-browser": "^14.1.0",
"@angular/platform-browser-dynamic": "^14.1.0",
"@angular/platform-server": "^14.1.0",
"@angular/router": "^14.1.0",
"@angular/service-worker": "^14.1.0",
"@angular/youtube-player": "^14.1.0",
"@aws-sdk/client-s3": "^3.118.1",
"@aws-sdk/s3-request-presigner": "^3.118.1",
"@angular/animations": "^14.2.8",
"@angular/cdk": "^14.2.6",
"@angular/common": "^14.2.8",
"@angular/compiler": "^14.2.8",
"@angular/core": "^14.2.8",
"@angular/forms": "^14.2.8",
"@angular/material": "^14.2.6",
"@angular/platform-browser": "^14.2.8",
"@angular/platform-browser-dynamic": "^14.2.8",
"@angular/platform-server": "^14.2.8",
"@angular/router": "^14.2.8",
"@angular/service-worker": "^14.2.8",
"@angular/youtube-player": "^14.2.6",
"@aws-sdk/client-s3": "^3.197.0",
"@aws-sdk/s3-request-presigner": "^3.197.0",
"@ngneat/dag": "^2.0.0",
"@ngrx/effects": "^14.0.2",
"@ngrx/entity": "^14.0.2",
"@ngrx/router-store": "^14.0.2",
"@ngrx/store": "^14.0.2",
"ace-builds": "^1.6.1",
"angular-google-tag-manager": "^1.6.0",
"angular-resizable-element": "^5.0.0",
"angular-split": "^13.2.0",
"@ngrx/effects": "^14.3.2",
"@ngrx/entity": "^14.3.2",
"@ngrx/router-store": "^14.3.2",
"@ngrx/store": "^14.3.2",
"ace-builds": "^1.12.3",
"angular-google-tag-manager": "^1.6.1",
"angular-resizable-element": "^6.0.0",
"angular-split": "^14.1.0",
"ansi-to-html": "^0.7.2",
"bootstrap": "^4.6.1",
"bootstrap": "^4.6.2",
"britecharts": "^2.18.0",
"curved-arrows": "^0.1.0",
"d3-selection": "^3.0.0",
"diff": "^5.1.0",
"dom-to-image": "^2.6.0",
"filesize": "^9.0.11",
"filesize": "^10.0.5",
"has-ansi": "^5.0.1",
"hocon-parser": "^1.0.1",
"jwt-decode": "^3.1.2",
"lodash": "^4.17.21",
"lucene": "^2.1.1",
"ngx-clipboard": "^15.1.0",
"ngx-color-picker": "^12.0.1",
"ngx-markdown-editor": "^4.0.0",
"ngx-color-picker": "^13.0.0",
"ngx-device-detector": "^4.0.1",
"ngx-markdown-editor": "^4.2.0",
"ngx-window-token": "^6.0.0",
"object-hash": "^3.0.0",
"primeicons": "^5.0.0",
"primeng": "^14.1.0",
"primeicons": "^6.0.1",
"primeng": "^14.1.2",
"process": "^0.11.10",
"rxjs": "^7.5.5",
"rxjs": "^7.5.7",
"string-to-color": "^2.2.2",
"tslib": "^2.4.0",
"url": "^0.11.0",
"uuid": "^8.3.2",
"zone.js": "~0.11.6"
"uuid": "^9.0.0",
"zone.js": "~0.11.8"
},
"devDependencies": {
"@angular-devkit/build-angular": "^14.1.0",
"@angular-devkit/core": "^14.1.0",
"@angular-devkit/schematics": "^14.1.0",
"@angular-devkit/schematics-cli": "^14.0.3",
"@angular-eslint/builder": "^14.0.2",
"@angular-eslint/eslint-plugin": "^14.0.2",
"@angular-eslint/eslint-plugin-template": "^14.0.2",
"@angular-eslint/schematics": "14.0.2",
"@angular-eslint/template-parser": "^14.0.2",
"@angular/cli": "^14.1.0",
"@angular/compiler-cli": "^14.1.0",
"@angular/language-service": "^14.1.0",
"@fortawesome/fontawesome-free": "^6.1.1",
"@ngrx/schematics": "^14.0.2",
"@ngrx/store-devtools": "^14.0.2",
"@types/d3-selection": "^3.0.2",
"@types/lodash": "^4.14.182",
"@types/node": "^16.11.19",
"@types/plotly.js": "^1.54.22",
"@angular-devkit/build-angular": "^14.2.7",
"@angular-devkit/core": "^14.2.7",
"@angular-devkit/schematics": "^14.2.7",
"@angular-devkit/schematics-cli": "^14.2.7",
"@angular-eslint/builder": "^14.1.2",
"@angular-eslint/eslint-plugin": "^14.1.2",
"@angular-eslint/eslint-plugin-template": "^14.1.2",
"@angular-eslint/schematics": "14.1.2",
"@angular-eslint/template-parser": "^14.1.2",
"@angular/cli": "^14.2.7",
"@angular/compiler-cli": "^14.2.8",
"@angular/language-service": "^14.2.8",
"@fortawesome/fontawesome-free": "^6.2.0",
"@ngrx/schematics": "^14.3.2",
"@ngrx/store-devtools": "^14.3.2",
"@types/d3-selection": "^3.0.3",
"@types/lodash": "^4.14.186",
"@types/node": "^16.18.2",
"@types/plotly.js": "^2.12.8",
"@types/uuid": "^8.3.4",
"@typescript-eslint/eslint-plugin": "^5.30.0",
"@typescript-eslint/parser": "^5.30.0",
"@typescript-eslint/eslint-plugin": "^5.41.0",
"@typescript-eslint/parser": "^5.41.0",
"codelyzer": "^6.0.2",
"eslint": "^8.18.0",
"eslint": "^8.26.0",
"eslint-plugin-import": "2.26.0",
"eslint-plugin-jsdoc": "39.3.3",
"eslint-plugin-jsdoc": "39.3.25",
"eslint-plugin-prefer-arrow": "1.2.3",
"typescript": "~4.7.4"
"typescript": "~4.8.4"
}
}

View File

@@ -11,7 +11,7 @@ export const routes: Routes = [
{
path: 'dashboard',
loadChildren: () => import('./features/dashboard/dashboard.module').then(m => m.DashboardModule),
data: {search: true},
data: {search: true, userFocus: true},
},
{
path: 'projects',

View File

@@ -21,10 +21,6 @@ export interface EventsGetPlotSampleRequest {
* Metric name
*/
metric: string;
/**
* Metric variant
*/
variant: string;
/**
* The iteration to bring plot from. If not specified then the latest reported plot is retrieved
*/

View File

@@ -25,4 +25,8 @@ export interface EventsNextPlotSampleRequest {
* If set then get the either previous variant event from the current iteration or (if does not exist) the last variant event from the previous iteration. Otherwise next variant event from the current iteration or first variant event from the next iteration
*/
navigate_earlier?: boolean;
/**
* If set then navigate to the next/previous iteration
*/
next_iteration?: boolean;
}

View File

@@ -18,9 +18,9 @@ export interface PlotSampleResponse {
*/
scroll_id?: string;
/**
* Plot event
* Plot events
*/
event?: object;
events?: Array<object>;
/**
* minimal valid iteration for the variant
*/

View File

@@ -25,17 +25,20 @@ export interface OrganizationGetEntitiesCountRequest {
* Search criteria for models
*/
models?: object;
/**
* Search criteria for dataviews
*/
dataviews?: object;
/**
* Search criteria for hyper datasets
*/
hyper_datasets?: object;
/**
* Search criteria for pipelines
*/
pipelines?: object;
/**
* Search criteria for datasets
*/
datasets: object;
/**
* If set to 'true' then hidden projects and tasks are included in the search results
*/
search_hidden?: boolean;
/**
* The list of users that were active in the project. If passes then the resulting projects are filtered to the ones that have tasks created by these users
*/
active_users?: string[];
}

View File

@@ -52,7 +52,7 @@ export class ProjectsEffects {
/* eslint-disable @typescript-eslint/naming-convention */
id: [action.projectId],
include_stats: true,
...(!showHidden && {include_stats_filter: {system_tags: ['-pipeline']}}),
...(!showHidden && {include_stats_filter: {system_tags: ['-pipeline', '-dataset']}}),
...(showOnlyUserWork && {active_users: [user.id]}),
...(showHidden && {search_hidden: true}),
...((action.example !== false || this.fetchingExampleExperiment === action.projectId) && {check_own_contents: true}),

View File

@@ -1,21 +1,33 @@
import {Injectable} from '@angular/core';
import {Actions, createEffect, ofType} from '@ngrx/effects';
import {map, switchMap} from 'rxjs/operators';
import {map, switchMap, withLatestFrom} from 'rxjs/operators';
import {getResultsCount, setResultsCount} from '@common/dashboard-search/dashboard-search.actions';
import {getEntityStatQuery} from '@common/dashboard-search/dashboard-search.effects';
import {ApiOrganizationService} from '~/business-logic/api-services/organization.service';
import {Store} from '@ngrx/store';
import {selectCurrentUser, selectShowOnlyUserWork} from '@common/core/reducers/users-reducer';
import {selectShowHidden} from '@common/core/reducers/projects.reducer';
@Injectable()
export class DashboardSearchEffects {
constructor(
private actions: Actions,
private store: Store,
public organizationApi: ApiOrganizationService) {
}
getResultsCount = createEffect(() => this.actions.pipe(
ofType(getResultsCount),
switchMap(action => this.organizationApi.organizationGetEntitiesCount({
withLatestFrom(
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser),
this.store.select(selectShowHidden),
),
switchMap(([action, userFocus, user, hidden]) => this.organizationApi.organizationGetEntitiesCount({
/* eslint-disable @typescript-eslint/naming-convention */
...(userFocus && {active_users: [user.id]}),
...(hidden && {search_hidden: true}),
...getEntityStatQuery(action)
})),
map(({tasks: experiments, ...rest}) =>

View File

@@ -3,18 +3,18 @@ import {
CommonProjectReadyForDeletion,
commonProjectsInitState,
commonProjectsReducers,
ICommonProjectsState
CommonProjectsState
} from '@common/projects/common-projects.reducer';
import {checkProjectForDeletion, resetReadyToDelete, setProjectReadyForDeletion} from '@common/projects/common-projects.actions';
export type IProjectReadyForDeletion = CommonProjectReadyForDeletion;
export interface IProjectsState extends ICommonProjectsState {
export interface ProjectsState extends CommonProjectsState {
projectReadyForDeletion: IProjectReadyForDeletion;
}
const projectsInitState: IProjectsState = {
const projectsInitState: ProjectsState = {
...commonProjectsInitState,
projectReadyForDeletion: {
project: null, experiments: null, models: null
@@ -41,7 +41,7 @@ export const projectsReducer = createReducer(
...commonProjectsReducers
);
export const projects = state => state.projects as IProjectsState;
export const projects = state => state.projects as ProjectsState;
// eslint-disable-next-line @typescript-eslint/no-unused-vars
export const selectShowHidden = createSelector(projects, (state) => false);

View File

@@ -12,7 +12,6 @@
&.first {
margin-top: 0;
}
}
.section {
@@ -21,7 +20,7 @@
.info {
margin-left: 12px;
color: $light-grey-blue;
color: $blue-400;
&:hover {
color: $purple;

View File

@@ -1,92 +1,52 @@
<div class="side-nav">
<div class="item logo">
<div class="item-icon">
<img src="assets/c-logo.svg?v=1" class="logo-a" alt="logo">
</div>
</div>
<div class="logo">
<img src="assets/c-logo.svg?v=1" class="logo-a" alt="logo" width="64" height="64">
</div>
<ng-container *ngIf="currentUser">
<a class="item d-block"
<ng-container *ngIf="currentUser">
<div #container class="items-container" [class.on-scrolling]="scrolling">
<a class="item al-ico-home"
routerLink="/dashboard"
routerLinkActive="active"
(click)="resetSearch()"
smTooltip="DASHBOARD"
[matTooltipShowDelay]="0"
matTooltipPosition="right"
>
<div class="item-icon">
<i class="al-icon al-ico-home al-color blue-300"></i>
</div>
<div class="caption">dashboard</div>
matTooltipPosition="right">
</a>
<a class="item d-block"
<a class="item al-ico-projects"
smTooltip="PROJECTS"
[matTooltipShowDelay]="0"
matTooltipPosition="right"
routerLink="/projects"
routerLinkActive="active"
>
<div class="item-icon">
<i class="al-icon al-ico-projects al-color blue-300"></i>
</div>
<div class="caption">projects</div>
routerLinkActive="active">
</a>
<a class="item d-block"
routerLink="/pipelines"
routerLinkActive="active"
smTooltip="PIPELINES"
[matTooltipShowDelay]="0"
matTooltipPosition="right">
<div class="item-icon">
<i class="al-icon al-ico-pipelines al-color blue-300"></i>
</div>
<div class="caption">pipelines</div>
</a>
<a class="item d-block"
<a class="item al-ico-datasets"
routerLink="/datasets"
routerLinkActive="active"
smTooltip="DATASETS"
[matTooltipShowDelay]="0"
matTooltipPosition="right">
<div class="item-icon">
<i class="al-icon al-ico-datasets al-color blue-300"></i>
</div>
<div class="caption">datasets</div>
</a>
<a class="item d-block"
<a class="item al-ico-pipelines"
routerLink="/pipelines"
routerLinkActive="active"
smTooltip="PIPELINES"
[matTooltipShowDelay]="0"
matTooltipPosition="right">
</a>
<a class="item al-ico-queues"
routerLink="/workers-and-queues"
routerLinkActive="active"
smTooltip="WORKERS & QUEUES"
[matTooltipShowDelay]="0"
matTooltipPosition="right">
<div class="item-icon">
<i class="al-icon al-ico-queues al-color blue-300"></i>
</div>
<div class="caption">workers & queues</div>
</a>
</ng-container>
<div *ngIf="currentUser" class="account">
<a *ngIf="environment.whiteLabelLink" class="item d-block" target="_blank"
[smTooltip]="environment.whiteLabelLink.tooltip"
[matTooltipShowDelay]="0"
matTooltipPosition="right"
[href]="environment.whiteLabelLink.link">
<div class="item-icon"
[style.background-image]="'url(' + environment.whiteLabelLink.logo + ')'"
style="width: 64px; height: 64px"
>
</div>
<div class="caption">Ignite</div>
</a>
<a *ngIf="environment.slackLink" class="item d-block" [href]="environment.slackLink"
target="_blank"
smTooltip="Community support on Slack" [matTooltipShowDelay]="0" matTooltipPosition="right">
<div class="item-icon">
<i class="al-icon al-ico-slack md al-color blue-300"></i>
</div>
<div class="caption">Slack</div>
</a>
</div>
</ng-container>
<div *ngIf="currentUser" class="account">
<a *ngIf="environment.slackLink" class="item" [href]="environment.slackLink"
target="_blank"
smTooltip="Community support on Slack" [matTooltipShowDelay]="0" matTooltipPosition="right">
<i class="i-slack-mark-color"></i>
</a>
</div>

View File

@@ -1,95 +1,82 @@
@import "../../webapp-common/shared/ui-components/styles/variables";
@import "variables";
$transition-delay: 0.1;
$transition-speed: 0.15;
$link-item-size: 64px;
$icon-size: 24px;
.side-nav {
z-index: 1000;
background-color: $blue-600;
:host {
display: flex;
flex-direction: column;
height: 100%;
width: $side-bar-close-width;
position: relative;
transition: width $transition-speed + s;
transition-delay: $transition-delay + s;
overflow: hidden;
.item.logo {
background: $eggplant-purple;
cursor: default;
.item-icon {
transition: opacity $transition-speed + $transition-delay + s;
opacity: 1;
height: 100%;
}
&:hover {
background-color: $eggplant-purple;
box-shadow: unset;
}
background-color: $blue-600;
.logo {
width: 64px;
height: 64px;
background: $blue-700;
}
a:hover {
color: $blue-250;
.items-container {
text-align: center;
overflow-x: hidden;
overflow-y: auto;
scrollbar-width: thin; /* Firefox */
-ms-overflow-style: none; /* IE 10+ */
&.on-scrolling {
box-shadow: 0 3px 3px rgba(0,0,0,0.3) inset, 0 -3px 3px rgba(0,0,0,0.3) inset;
}
}
.items-container::-webkit-scrollbar-track {
box-shadow: none;
-webkit-box-shadow: none !important;
background-color: transparent !important;
}
.items-container::-webkit-scrollbar {
width: 2px !important;
background-color: transparent;
}
.items-container::-webkit-scrollbar-thumb {
border-radius: unset;
border: none;
}
.item {
width: 100%;
height: $side-bar-close-width;
line-height: $side-bar-close-width;
color: #fff;
text-align: center;
cursor: pointer;
.fa-24 {
font-size: 24px;
display: flex;
align-items: center;
justify-content: center;
width: $link-item-size;
height: $link-item-size;
color: $blue-300;
margin: 0 auto;
// ICON SIZE
&:before {
font-size: $icon-size;
}
&:hover {
color: $blue-200;
background-color: $blue-500;
box-shadow: inset 0 -1px 0 0 #252b3d;
.caption {
opacity: 1;
}
text-decoration: none;
}
.item-icon {
width: $side-bar-close-width;
display: inline-block;
position: absolute;
left: 0;
color: #a7b2d8;
i{
line-height: 64px;
height: 64px;
width: 64px;
}
&.active:before {
color: $neon-yellow
}
}
&.active {
.item-icon i {
color: $neon-yellow
}
}
.caption {
transition: opacity $transition-speed + s;
position: absolute;
left: $side-bar-close-width;
text-transform: uppercase;
font-size: 15px;
opacity: 0.4;
color: $blue-250;
}
.i-slack-mark-color {
width: $icon-size;
height: $icon-size;
background-size: $icon-size;
}
.account {
position: absolute;
bottom: 0;
width: 100%;
display: flex;
flex-direction: column;
margin-top: auto;
}
}

View File

@@ -1,28 +1,43 @@
import { selectCurrentUser } from './../../webapp-common/core/reducers/users-reducer';
import {Component} from '@angular/core';
import {selectCurrentUser} from '@common/core/reducers/users-reducer';
import {AfterViewInit, ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, ViewChild} from '@angular/core';
import {Store} from '@ngrx/store';
import {selectSelectedProjectId} from '../../webapp-common/core/reducers/projects.reducer';
import {Observable} from 'rxjs';
import {Router} from '@angular/router';
import {ConfigurationService} from '../../webapp-common/shared/services/configuration.service';
import {searchDeactivate} from '../../webapp-common/dashboard-search/dashboard-search.actions';
import {selectSelectedProjectId} from '@common/core/reducers/projects.reducer';
import {fromEvent, Observable} from 'rxjs';
import {ConfigurationService} from '@common/shared/services/configuration.service';
import {searchDeactivate} from '@common/dashboard-search/dashboard-search.actions';
@Component({
selector : 'sm-side-nav',
templateUrl: './side-nav.component.html',
styleUrls : ['./side-nav.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class SideNavComponent {
export class SideNavComponent implements AfterViewInit {
public selectedProjectId$: Observable<any>;
currentUser: any;
environment = ConfigurationService.globalEnvironment;
public scrolling: boolean;
@ViewChild('container') container: ElementRef<HTMLDivElement>;
constructor(public store: Store<any>, private router: Router) {
constructor(public store: Store<any>, private cdr: ChangeDetectorRef) {
this.selectedProjectId$ = this.store.select(selectSelectedProjectId);
this.store.select(selectCurrentUser).subscribe((res) => this.currentUser = res);
fromEvent(window, 'resize').subscribe(() => {
const scrolling = this.container.nativeElement.scrollHeight > this.container.nativeElement.clientHeight;
if (scrolling !== this.scrolling) {
this.scrolling = scrolling;
this.cdr.detectChanges();
}
this.scrolling = scrolling;
});
}
ngAfterViewInit(): void {
this.scrolling = this.container.nativeElement.scrollHeight > this.container.nativeElement.clientHeight;
}
public resetSearch() {
this.store.dispatch(searchDeactivate());

View File

@@ -2,7 +2,7 @@
@font-face {
font-family: '#{$icomoon-font-family}';
src: url('./#{$icomoon-font-family}.ttf?ttf?vsfnj4') format('truetype');
src: url('./#{$icomoon-font-family}.ttf?84hl5c') format('truetype');
font-weight: normal;
font-style: normal;
font-display: block;
@@ -23,6 +23,21 @@
-moz-osx-font-smoothing: grayscale;
}
.al-ico-gpu {
&:before {
content: $al-ico-gpu;
}
}
.al-ico-project-path {
&:before {
content: $al-ico-project-path;
}
}
.al-ico-tree-view {
&:before {
content: $al-ico-tree-view;
}
}
.al-ico-sort-asc {
&:before {
content: $al-ico-sort-asc;
@@ -721,52 +736,53 @@
.al-ico-filter-on .path1 {
&:before {
content: $al-ico-filter-on-path1;
color: rgb(211, 255, 0);
color: $neon-yellow; // don't overwrite
// color: rgb(211, 255, 0);
}
}
.al-ico-filter-on .path2 {
&:before {
content: $al-ico-filter-on-path2;
margin-left: -1em;
color: rgb(132, 146, 194);
color: $blue-300; // don't overwrite
}
}
.al-ico-filter-off {
&:before {
content: $al-ico-filter-off;
color: #8492c2;
color: $blue-400; // don't overwrite
}
}
.al-ico-sort-off {
&:before {
content: $al-ico-sort-off;
color: #5a658e;
color: $blue-400; // don't overwrite
}
}
.al-ico-sort-on-down .path1 {
&:before {
content: $al-ico-sort-on-down-path1;
color: rgb(90, 101, 142);
color: $blue-400; // don't overwrite
}
}
.al-ico-sort-on-down .path2 {
&:before {
content: $al-ico-sort-on-down-path2;
margin-left: -1em;
color: rgb(195, 205, 240);
color: $blue-200; // don't overwrite
}
}
.al-ico-sort-on-up .path1 {
&:before {
content: $al-ico-sort-on-up-path1;
color: rgb(90, 101, 142);
color: $blue-400; // don't overwrite
}
}
.al-ico-sort-on-up .path2 {
&:before {
content: $al-ico-sort-on-up-path2;
margin-left: -1em;
color: rgb(195, 205, 240);
color: $blue-200; // don't overwrite
}
}
.al-ico-arrow-left {
@@ -992,27 +1008,28 @@
.al-ico-auto-refresh-play .path1 {
&:before {
content: $al-ico-auto-refresh-play-path1;
color: rgb(90, 101, 142);
color: $blue-300; // don't overwrite
}
}
.al-ico-auto-refresh-play .path2 {
&:before {
content: $al-ico-auto-refresh-play-path2;
margin-left: -1em;
color: rgb(211, 253, 0);
color: $neon-yellow; // don't overwrite
}
}
.al-ico-auto-refresh-pause .path1 {
&:before {
content: $al-ico-auto-refresh-pause-path1;
color: rgb(90, 101, 142);
color: $blue-300; // don't overwrite
}
}
.al-ico-auto-refresh-pause .path2 {
&:before {
content: $al-ico-auto-refresh-pause-path2;
margin-left: -1em;
color: rgb(211, 253, 0);
color: $neon-yellow; // don't overwrite
// color: rgb(211, 253, 0);
}
}
.al-ico-sqr-ok {
@@ -1068,14 +1085,15 @@
.al-ico-filter-reset .path1 {
&:before {
content: $al-ico-filter-reset-path1;
color: rgb(90, 101, 142);
color: $blue-300; // don't overwrite
}
}
.al-ico-filter-reset .path2 {
&:before {
content: $al-ico-filter-reset-path2;
margin-left: -1em;
color: rgb(237, 138, 131);
color: lighten($failed-red, 10%); // don't overwrite
// color: rgb(237, 138, 131);
}
}
.al-ico-version-label {
@@ -1111,14 +1129,15 @@
.al-ico-settings-alert .path1 {
&:before {
content: $al-ico-settings-alert-path1;
color: rgb(0, 0, 0);
color: $black; // don't overwrite
}
}
.al-ico-settings-alert .path2 {
&:before {
content: $al-ico-settings-alert-path2;
margin-left: -1em;
color: rgb(211, 255, 0);
color: $neon-yellow; // don't overwrite
// color: rgb(211, 255, 0);
}
}
.al-ico-platform {

View File

@@ -1,6 +1,9 @@
$icomoon-font-family: "trains" !default;
$icomoon-font-path: "fonts" !default;
$al-ico-gpu: "\e9e7";
$al-ico-project-path: "\e9e6";
$al-ico-tree-view: "\e9e5";
$al-ico-sort-asc: "\e9e3";
$al-ico-sort-desc: "\e9e4";
$al-ico-grid-view: "\e9e0";

View File

@@ -0,0 +1,6 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24">
<path d="M5.06,15.17c0,1.39-1.13,2.52-2.52,2.52S.02,16.56,.02,15.17s1.13-2.52,2.52-2.52h2.52v2.52Zm1.26,0c0-1.39,1.13-2.52,2.52-2.52s2.52,1.13,2.52,2.52v6.3c0,1.39-1.13,2.52-2.52,2.52s-2.52-1.13-2.52-2.52v-6.3Z" fill="#e01e5a"/>
<path d="M8.84,5.05c-1.39,0-2.52-1.13-2.52-2.52S7.45,0,8.84,0s2.52,1.13,2.52,2.52v2.52h-2.52Zm0,1.28c1.39,0,2.52,1.13,2.52,2.52s-1.13,2.52-2.52,2.52H2.52c-1.39,0-2.52-1.13-2.52-2.52s1.13-2.52,2.52-2.52h6.32Z" fill="#36c5f0"/>
<path d="M18.94,8.85c0-1.39,1.13-2.52,2.52-2.52s2.52,1.13,2.52,2.52-1.13,2.52-2.52,2.52h-2.52v-2.52Zm-1.26,0c0,1.39-1.13,2.52-2.52,2.52s-2.52-1.13-2.52-2.52V2.53c0-1.39,1.13-2.52,2.52-2.52s2.52,1.13,2.52,2.52c0,0,0,6.32,0,6.32Z" fill="#2eb67d"/>
<path d="M15.16,18.95c1.39,0,2.52,1.13,2.52,2.52s-1.13,2.52-2.52,2.52-2.52-1.13-2.52-2.52v-2.52h2.52Zm0-1.26c-1.39,0-2.52-1.13-2.52-2.52s1.13-2.52,2.52-2.52h6.32c1.39,0,2.52,1.13,2.52,2.52s-1.13,2.52-2.52,2.52h-6.32Z" fill="#ecb22e"/>
</svg>

After

Width:  |  Height:  |  Size: 1011 B

File diff suppressed because one or more lines are too long

View File

@@ -20,8 +20,8 @@
</sm-search>
</span>
<ng-container *ngIf="searchActive">
<i *ngIf="(isSearching$ | async); else searchIcon" class="fa fa-times pointer" (click)="clearSearch()" smClickStopPropagation></i>
<i *ngIf="(isSearching$ | async); else searchIcon" class="al-icon al-ico-dialog-x pointer" (click)="clearSearch()" smClickStopPropagation></i>
<ng-template #searchIcon>
<i class="fa fa-search pointer" (click)="openSearch()"></i>
<i class="al-icon al-ico-search pointer" (click)="openSearch()"></i>
</ng-template>
</ng-container>

View File

@@ -1,4 +1,4 @@
@import "../../../shared/ui-components/styles/variables";
@import "variables";
:host {
position: relative;
@@ -41,9 +41,8 @@
}
i.fa {
color: $blue-grey;
font-size: 18px;
.al-icon {
color: $blue-300;
margin-left: auto;
width: 32px;
height: 32px;
@@ -55,8 +54,8 @@
color: $blue-200;
}
}
i.fa-times {
margin-right:4px;
.al-ico-dialog-x {
transform: scale(0.9);
}
::ng-deep sm-search.regex-error input {

View File

@@ -2,13 +2,13 @@
@import "angular-notifier/styles.scss";
@import "angular-notifier/styles/themes/theme-material.scss";
@import "shared/ui-components/styles/notifications";
@import "~britecharts/src/styles/charts/line";
@import "~britecharts/src/styles/charts/donut";
@import "~britecharts/src/styles/common/legend";
@import "~britecharts/src/styles/common/tooltip";
@import "~britecharts/src/styles/common/axes";
@import "~britecharts/src/styles/common/grid";
// @import "~ngx-markdown-editor/assets/highlight.js/agate.min.css";
@import "britecharts/src/styles/charts/line";
@import "britecharts/src/styles/charts/donut";
@import "britecharts/src/styles/common/legend";
@import "britecharts/src/styles/common/tooltip";
@import "britecharts/src/styles/common/axes";
@import "britecharts/src/styles/common/grid";
// @import "ngx-markdown-editor/assets/highlight.js/agate.min.css";
@import "shared/ui-components/styles/material-palette";
@import "assets/fonts/trains-icons.scss";
@import "layout/layout";
@@ -95,6 +95,14 @@ $sm-theme: mat.define-light-theme($sm-theme-primary, $sm-theme-accent, $sm-theme
@include mat.pseudo-checkbox-color($light-theme);
@include mat.divider-color($light-theme);
mat-progress-bar {
border-radius: 4px;
box-shadow: 0 0 0 1px $white, 0 0 0 3px lighten($purple, 30%);
}
.mat-progress-bar-fill::after {
background-color: lighten($purple, 10%);
}
.mat-checkbox-frame,
.mat-radio-outer-circle {
border-color: $purple;
@@ -289,7 +297,7 @@ button {
}
.color-neon-yellow {
color: $neon-yellow;
color: $neon-yellow !important;
}
.background-neon-green {
@@ -511,9 +519,10 @@ html {
.sm-menu-header {
text-align: center;
padding: 15px 15px;
padding: 12px;
background: $blue-25;
color: $blue-400;
border-bottom: 1px solid $blue-200;
}
.mat-menu-content .mat-menu-item {
@@ -576,36 +585,6 @@ $type-colors: (
date: #05668D,
);
.dark-theme {
.ngx-json-viewer {
background-color: $blue-800;
overflow: unset !important;
height: 99% !important;
font-size: 11px !important;
line-height: 11px !important;
.segment .segment-main {
.toggler {
font-size: 16px !important;
line-height: 4px !important;
}
.segment-key {
color: $blue-250 !important;
}
}
}
@each $type, $color in $type-colors {
.segment-type-#{$type} > .segment-main > .segment-value {
color: $color !important;
}
}
.segment-type-link > .segment-main > .segment-value > a {
color: #ff8400 !important;
}
}
.mat-expansion-panel.allegro-style {
width: 100%;
@@ -731,7 +710,7 @@ button.btn.button-outline-dark {
.sm-card-list-layout {
display: grid;
grid-template-columns: repeat(auto-fit, 352px);
grid-gap: 32px 24px;
grid-gap: 24px;
padding: 0 24px 24px;
justify-content: center;
@@ -751,7 +730,7 @@ button.btn.button-outline-dark {
.sm-card-list-header {
display: flex;
justify-content: space-between;
padding-top: $projects-header-padding;
padding: $projects-header-padding 0 $projects-header-padding * 0.5;
.recent-title {
display: flex;

View File

@@ -67,6 +67,7 @@ export const ICONS = {
ARROW_DOWN: 'al-ico-ico-chevron-down',
ARROW_UP: 'al-ico-ico-chevron-up',
RUN: 'al-ico-run',
METADATA: 'al-ico-metadata',
};
export type IconNames = keyof typeof ICONS;

View File

@@ -1,11 +1,14 @@
import {HTTP_PREFIX} from '../../../app.constants';
import {omit} from 'lodash/fp';
import {HTTP_PREFIX} from '~/app.constants';
import {HttpErrorResponse} from '@angular/common/http';
import {createAction, props} from '@ngrx/store';
export const requestFailed = createAction(
HTTP_PREFIX + 'REQUEST_FAILED',
(err: HttpErrorResponse) => ({err: omit(['headers'], err)})
(err: HttpErrorResponse) => {
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const {headers, ...others} = err;
return {err: {...others, error: {meta: others.error.meta}}};
}
);
export const apiRequest = createAction(

View File

@@ -85,7 +85,8 @@ export const setDeep = createAction(
);
export const getTags = createAction(
PROJECTS_PREFIX + '[get tags]'
PROJECTS_PREFIX + '[get tags]',
(project = null) => ({projectId: project})
);
export const getCompanyTags = createAction(

View File

@@ -53,6 +53,12 @@ export class ProjectsEffects {
map(action => activeLoader(action.type))
));
setProject = createEffect(() => this.actions$.pipe(
ofType(actions.setSelectedProject),
switchMap(action => [actions.getCompanyTags()]
.concat(!!action.project?.id ? [actions.getTags(action.project.id)] : []))
));
getProjects$ = createEffect(() => this.actions$.pipe(
ofType(actions.getAllSystemProjects),
withLatestFrom(
@@ -154,22 +160,28 @@ export class ProjectsEffects {
ofType(actions.getTags),
withLatestFrom(this.store.select(selectRouterParams).pipe(
map(params => (params === null || params?.projectId === '*') ? [] : [params.projectId]))),
switchMap(([action, projects]) => forkJoin([
this.projectsApi.projectsGetTaskTags({projects}),
this.projectsApi.projectsGetModelTags({projects})]
).pipe(
map((res: [ProjectsGetTaskTagsResponse, ProjectsGetModelTagsResponse]) =>
Array.from(new Set(res[0].tags.concat(res[1].tags))).sort()),
mergeMap((tags: string[]) => [
actions.setTags({tags}),
deactivateLoader(action.type)
]),
catchError(error => [
requestFailed(error),
deactivateLoader(action.type),
setServerError(error, null, 'Fetch tags failed')]
)
))
switchMap(([action, projects]) => {
const ids = action?.projectId ? [action.projectId] : projects;
if (ids.length === 0 || !ids[0]) {
return EMPTY;
}
return forkJoin([
this.projectsApi.projectsGetTaskTags({projects: action?.projectId ? [action.projectId] : projects}),
this.projectsApi.projectsGetModelTags({projects: action?.projectId ? [action.projectId] : projects})]
).pipe(
map((res: [ProjectsGetTaskTagsResponse, ProjectsGetModelTagsResponse]) =>
Array.from(new Set(res[0].tags.concat(res[1].tags))).sort()),
mergeMap((tags: string[]) => [
actions.setTags({tags}),
deactivateLoader(action.type)
]),
catchError(error => [
requestFailed(error),
deactivateLoader(action.type),
setServerError(error, null, 'Fetch tags failed')]
)
);
})
));
openMoreInfoPopupEffect = createEffect(() => this.actions$.pipe(

View File

@@ -31,6 +31,7 @@ import {escapeRegex} from '../shared/utils/shared-utils';
import {isEqual} from 'lodash/fp';
import {activeSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
import {EmptyAction} from '~/app.constants';
import {selectCurrentUser, selectShowOnlyUserWork} from '@common/core/reducers/users-reducer';
export const getEntityStatQuery = action => ({
/* eslint-disable @typescript-eslint/naming-convention */
@@ -46,26 +47,27 @@ export const getEntityStatQuery = action => ({
fields: ['name', 'id']
},
type: ['__$not', 'annotation_manual', '__$not', 'annotation', '__$not', 'dataset_import'],
system_tags: ['-archived', '-pipeline', '-dataset']
system_tags: ['-archived', '-pipeline', '-dataset'],
},
models: {_any_: {
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
fields: ['name', 'id']
}},
},
},
datasets: {_any_: {
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
fields: ['basename', 'id']
},
search_hidden: true,
system_tags: ['dataset'],
name: '/\\.datasets/'
name: '/\\.datasets/',
},
pipelines: {_any_: {
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
fields: ['basename', 'id']
},
search_hidden: true,
system_tags: ['pipeline']
system_tags: ['pipeline'],
}
/* eslint-enable @typescript-eslint/naming-convention */
});
@@ -90,7 +92,8 @@ export class DashboardSearchEffects {
ofType(searchStart),
withLatestFrom(
this.store.select(selectActiveSearch),
this.store.select(selectSearchTerm)),
this.store.select(selectSearchTerm)
),
mergeMap(([action, active, term]) => {
const actionsToFire = [];
if (!active) {
@@ -108,7 +111,8 @@ export class DashboardSearchEffects {
getCurrentPageResults = createEffect(() => this.actions.pipe(
ofType(getCurrentPageResults),
withLatestFrom(
this.store.select(selectSearchTerm)),
this.store.select(selectSearchTerm)
),
map(([action, term]) => {
switch (action.activeLink) {
case activeSearchLink.experiments:
@@ -129,8 +133,12 @@ export class DashboardSearchEffects {
searchProjects = createEffect(() => this.actions.pipe(
ofType(searchProjects),
withLatestFrom(this.store.select(selectSearchScrollIds)),
switchMap(([action, scrollIds]) => this.projectsApi.projectsGetAllEx({
withLatestFrom(
this.store.select(selectSearchScrollIds),
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser),
),
switchMap(([action, scrollIds, userFocus, user]) => this.projectsApi.projectsGetAllEx({
_any_: {
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
fields: ['basename', 'id']
@@ -140,6 +148,7 @@ export class DashboardSearchEffects {
stats_for_state: ProjectsGetAllExRequest.StatsForStateEnum.Active,
scroll_id: scrollIds?.[activeSearchLink.projects] || null,
size: SEARCH_PAGE_SIZE,
...(userFocus && {active_users: [user.id]}),
include_stats: true,
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'basename']
/* eslint-enable @typescript-eslint/naming-convention */
@@ -150,8 +159,12 @@ export class DashboardSearchEffects {
searchPipelines = createEffect(() => this.actions.pipe(
ofType(searchPipelines),
withLatestFrom(this.store.select(selectSearchScrollIds)),
switchMap(([action, scrollIds]) => this.projectsApi.projectsGetAllEx({
withLatestFrom(
this.store.select(selectSearchScrollIds),
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser),
),
switchMap(([action, scrollIds, userFocus, user]) => this.projectsApi.projectsGetAllEx({
_any_: {
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
fields: ['basename', 'id']
@@ -163,6 +176,7 @@ export class DashboardSearchEffects {
stats_for_state: ProjectsGetAllExRequest.StatsForStateEnum.Active,
scroll_id: scrollIds?.[activeSearchLink.pipelines] || null,
size: SEARCH_PAGE_SIZE,
...(userFocus && {active_users: [user.id]}),
include_stats: true,
only_fields: ['name', 'company', 'user', 'created', 'default_output_destination', 'tags', 'system_tags', 'basename']
/* eslint-enable @typescript-eslint/naming-convention */
@@ -173,8 +187,12 @@ export class DashboardSearchEffects {
searchOpenDatasets = createEffect(() => this.actions.pipe(
ofType(searchOpenDatasets),
withLatestFrom(this.store.select(selectSearchScrollIds)),
switchMap(([action, scrollIds]) => this.projectsApi.projectsGetAllEx({
withLatestFrom(
this.store.select(selectSearchScrollIds),
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser),
),
switchMap(([action, scrollIds, userFocus, user]) => this.projectsApi.projectsGetAllEx({
/* eslint-disable @typescript-eslint/naming-convention */
_any_: {
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
@@ -187,6 +205,7 @@ export class DashboardSearchEffects {
stats_for_state: ProjectsGetAllExRequest.StatsForStateEnum.Active,
scroll_id: scrollIds?.[activeSearchLink.openDatasets] || null,
size: SEARCH_PAGE_SIZE,
...(userFocus && {active_users: [user.id]}),
include_dataset_stats: true,
stats_with_children: false,
include_stats: true,
@@ -200,8 +219,12 @@ export class DashboardSearchEffects {
searchModels = createEffect(() => this.actions.pipe(
ofType(searchModels),
withLatestFrom(this.store.select(selectSearchScrollIds)),
switchMap(([action, scrollIds]) => this.modelsApi.modelsGetAllEx({
withLatestFrom(
this.store.select(selectSearchScrollIds),
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser),
),
switchMap(([action, scrollIds, userFocus, user]) => this.modelsApi.modelsGetAllEx({
/* eslint-disable @typescript-eslint/naming-convention */
_any_: {
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
@@ -209,6 +232,7 @@ export class DashboardSearchEffects {
},
scroll_id: scrollIds?.[activeSearchLink.models] || null,
size: SEARCH_PAGE_SIZE,
...(userFocus && {user: [user.id]}),
system_tags: ['-archived'],
include_stats: true,
only_fields: ['ready', 'created', 'framework', 'user.name', 'name', 'parent.name', 'task.name', 'id', 'company']
@@ -220,8 +244,12 @@ export class DashboardSearchEffects {
searchExperiments = createEffect(() => this.actions.pipe(
ofType(searchExperiments),
withLatestFrom(this.store.select(selectSearchScrollIds)),
switchMap(([action, scrollIds]) => this.experimentsApi.tasksGetAllEx({
withLatestFrom(
this.store.select(selectSearchScrollIds),
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser),
),
switchMap(([action, scrollIds, userFocus, user]) => this.experimentsApi.tasksGetAllEx({
/* eslint-disable @typescript-eslint/naming-convention */
_any_: {
...(action.query && {pattern: action.regExp ? action.query : escapeRegex(action.query)}),
@@ -229,6 +257,7 @@ export class DashboardSearchEffects {
},
scroll_id: scrollIds?.[activeSearchLink.experiments] || null,
size: SEARCH_PAGE_SIZE,
...(userFocus && {user: [user.id]}),
only_fields: EXPERIMENT_SEARCH_ONLY_FIELDS,
type: ['__$not', 'annotation_manual', '__$not', 'annotation', '__$not', 'dataset_import'],
system_tags: ['-archived', '-pipeline', '-dataset']

View File

@@ -6,8 +6,9 @@
<div>
<button *ngIf="(recentProjectsList$ | async).length >= cardsInRow || overflow"
class="btn btn-cml-primary d-flex align-items-center"
data-id="New Project"
(click)="openCreateProjectDialog()">
<i class="al-icon al-color sm blue-400 al-ico-add mr-2"></i>NEW PROJECT
<i class="al-icon sm al-ico-add mr-2"></i>NEW PROJECT
</button>
</div>
</div>

View File

@@ -4,7 +4,7 @@ import {Model} from '~/business-logic/model/models/model';
import {clearSearchResults, getCurrentPageResults, searchClear, searchDeactivate, searchStart} from '../dashboard-search/dashboard-search.actions';
import {IRecentTask} from './common-dashboard.reducer';
import {ITask} from '~/business-logic/model/al-task';
import {Observable, Subscription} from 'rxjs';
import {combineLatest, Observable, Subscription} from 'rxjs';
import {SearchState, selectSearchQuery} from '../common-search/common-search.reducer';
import {Store} from '@ngrx/store';
import {
@@ -17,6 +17,7 @@ import {isExample} from '../shared/utils/shared-utils';
import {activeLinksList, ActiveSearchLink, activeSearchLink} from '~/features/dashboard-search/dashboard-search.consts';
import {Component, OnDestroy, OnInit} from '@angular/core';
import {Router} from '@angular/router';
import { selectShowOnlyUserWork } from '@common/core/reducers/users-reducer';
@Component({
selector: 'sm-dashboard-search-base',
@@ -69,9 +70,7 @@ export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
public ngOnInit(): void {
this.allResultsSubscription = this.resultsCount$.pipe(
filter(resultsCount => !!resultsCount),
).subscribe((resultsCount) => {
return this.setFirstActiveLink(resultsCount);
});
).subscribe((resultsCount) => this.setFirstActiveLink(resultsCount));
}
ngOnDestroy(): void {
@@ -89,9 +88,12 @@ export class DashboardSearchBaseComponent implements OnInit, OnDestroy{
syncAppSearch() {
this.store.dispatch(initSearch({payload: 'Search for all'}));
this.searchSubs = this.searchQuery$
this.searchSubs = combineLatest([
this.searchQuery$,
this.store.select(selectShowOnlyUserWork),
])
.pipe(skip(1))
.subscribe(query => this.searchTermChanged(query?.query, query?.regExp));
.subscribe(([query]) => this.searchTermChanged(query?.query, query?.regExp));
this.searchSubs.add(this.store.select(selectSearchScrollIds).subscribe(scrollIds => this.scrollIds = scrollIds));
}

View File

@@ -14,10 +14,12 @@
pattern="^[^/]+$"
[inlineDisabled]="true"
(textChanged)="prepareProjectNameForChange($event)"
(inlineActiveStateChanged)="projectNameEditActiveChanged($event)"
>
<span class="project-name" [smTooltip]="project.name">{{project.name | shortProjectName}}</span>
<span class="d-flex align-items-center"><span class="project-name"
[smTooltip]="project.name | cleanProjectPath">{{project.name | shortProjectName}}</span>
<i *ngIf="project.name | cleanProjectPath: false" [smTooltip]="project.name | cleanProjectPath:false"
class="al-icon al-ico-project-path sm ml-2"></i></span>
</sm-inline-edit>
<sm-pipeline-card-menu
class="menu-wrapper"
@@ -31,7 +33,9 @@
></sm-pipeline-card-menu>
</div>
<div *ngIf="project.last_update; else: noRun" class="last-run">Updated {{project.last_update | timeAgo}}</div>
<ng-template #noRun><div class="last-run"></div></ng-template>
<ng-template #noRun>
<div class="last-run"></div>
</ng-template>
</div>
<div class="d-flex justify-content-around w-100">
<sm-circle-counter

View File

@@ -8,7 +8,7 @@
}
.project-name {
max-width: 280px;
max-width: 250px;
text-overflow: ellipsis;
overflow: hidden;
white-space: nowrap;

View File

@@ -91,7 +91,7 @@
[filetype="html"]::before,
[filetype="yaml"]::before,
[filetype="json"]::before {
content: "\e9a4"; // code-icon
content: "\e9a4"; // al-ico-code
color: #a35151;
}
}

View File

@@ -34,10 +34,13 @@ export class SimpleDatasetVersionInfoComponent extends PipelineControllerInfoCom
) {
super(_dagManager, store, cdr, zone);
}
convertPipelineToDagModel(pipeline): PipelineItem[] {
const res = super.convertPipelineToDagModel(pipeline);
if (res?.length > 0) {
window.setTimeout(() => this.selectStep(last(res)), 1000);
} else if (this.infoData?.id){
this.store.dispatch(getSelectedPipelineStep({id: this.infoData.id}));
}
return res;
}

View File

@@ -1,5 +1,5 @@
<div *ngIf="activateFromMenuButton" [matMenuTriggerFor]="experimentMenu"
class="p-0 cell menu-button al-icon sm-md al-color light-grey-blue al-ico-bars-menu"
class="p-0 cell menu-button al-icon sm-md al-color blue-300 al-ico-bars-menu"
></div>
<div *ngIf="!activateFromMenuButton" #contextTrigger
style="visibility: hidden; position: fixed"
@@ -9,7 +9,7 @@
</div>
<mat-menu #experimentMenu="matMenu" [hasBackdrop]="false">
<button *ngIf="tableMode" mat-menu-item (click)="toggleDetails()">
<i [class]="'al-icon '+ ICONS.DETAILS + ' sm-md'"></i>Details
<i [class]="'al-icon '+ icons.DETAILS + ' sm-md'"></i>Details
</button>
<hr *ngIf="tableMode">
<button mat-menu-item
@@ -17,19 +17,19 @@
[disabled]="selectedDisableAvailable[menuItems.tags]?.disable"
(menuOpened)="tagMenuOpened()"
(menuClosed)="tagMenuClosed()">
<i [class]="'al-icon ' + ICONS.TAG + ' sm-md'">
<i [class]="'al-icon ' + icons.TAG + ' sm-md'">
</i><span>{{selectedDisableAvailable[menuItems.tags]?.available | menuItemText : 'Add Tag' : selectedDisableAvailableIsMultiple }}</span>
</button>
<button mat-menu-item (click)="restoreArchive(entityTypeEnum.dataset)"
[disabled]="selectedDisableAvailable[menuItems.archive]?.disable">
<i class="al-icon sm-md" [class]="isArchive ? ICONS.RESTORE : ICONS.ARCHIVE"></i>
<i class="al-icon sm-md" [class]="isArchive ? icons.RESTORE : icons.ARCHIVE"></i>
{{selectedDisableAvailable[menuItems.archive]?.available | menuItemText : isArchive ? 'Restore from Archive' : 'Archive' : selectedDisableAvailableIsMultiple}}
</button>
<button mat-menu-item (click)="deleteExperimentPopup(entityTypeEnum.dataset, true)"
[disabled]="selectedDisableAvailable[menuItems.delete]?.disable" *ngIf="isArchive">
<i [class]="'al-icon ' + ICONS.REMOVE + ' sm-md'"></i>
<i [class]="'al-icon ' + icons.REMOVE + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.delete]?.available | menuItemText : 'Delete' : selectedDisableAvailableIsMultiple}}
</button>
</mat-menu>

View File

@@ -50,7 +50,8 @@ export class SimpleDatasetVersionsComponent extends ControllersComponent impleme
if (!this.route.snapshot.firstChild?.params.versionId) {
this.store.dispatch(experimentsActions.experimentSelectionChanged({
experiment: this.firstExperiment,
project: this.selectedProject
project: this.selectedProject,
replaceURL: true
}));
}
}

View File

@@ -8,7 +8,7 @@
<button
class="btn btn-cml-primary d-flex align-items-center"
(click)="createDataset()">
<i class="al-icon al-color blue-400 al-ico-add sm mr-2"></i>NEW DATASET
<i class="al-icon al-ico-add sm mr-2"></i>NEW DATASET
</button>
</sm-projects-header>
<ng-container *ngFor="let project of projects; trackBy: trackById ;let last=last">

View File

@@ -20,7 +20,7 @@ export const getDebugImagesMetrics = createAction(
export const refreshDebugImagesMetrics = createAction(
DEBUG_IMAGES_PREFIX + 'REFRESH_DEBUG_IMAGES_METRICS',
props<{ tasks: string[], autoRefresh?: boolean }>()
props<{ tasks: string[]; autoRefresh?: boolean }>()
);
export const fetchExperiments = createAction(
@@ -69,7 +69,10 @@ export const setBeginningOfTime = createAction(
);
export const getDebugImageSample = createAction(DEBUG_IMAGES_PREFIX + 'GET_DEBUG_IMAGES_FOR_ITERATION', props<{ task: string; metric: string; variant: string; iteration: number; isAllMetrics: boolean }>());
export const getNextDebugImageSample = createAction(DEBUG_IMAGES_PREFIX + 'GET_NEXT_DEBUG_IMAGE', props<{ task: string; navigateEarlier: boolean }>());
export const getNextDebugImageSample = createAction(
DEBUG_IMAGES_PREFIX + 'GET_NEXT_DEBUG_IMAGE',
props<{ task: string; navigateEarlier: boolean; iteration?: boolean }>()
);
export const setCurrentDebugImage = createAction(DEBUG_IMAGES_PREFIX + 'SET_DEBUG_IMAGES_FOR_ITERATION', props<{ event: any }>());
export const setDebugImageViewerScrollId = createAction(DEBUG_IMAGES_PREFIX + 'SET_DEBUG_IMAGE_VIEWER_SCROLL_ID', props<{ scrollId: string }>());
export const setDebugImageIterations = createAction(DEBUG_IMAGES_PREFIX + 'SET_DEBUG_IMAGE_ITERATIONS', props<EventsGetDebugImageIterationsResponse>());

View File

@@ -178,7 +178,8 @@ export class DebugImagesEffects {
/* eslint-disable @typescript-eslint/naming-convention */
task: action.task,
scroll_id: scrollId,
navigate_earlier: action.navigateEarlier
navigate_earlier: action.navigateEarlier,
...(action.iteration && {next_iteration: true})
/* eslint-enable @typescript-eslint/naming-convention */
})
.pipe(
@@ -191,7 +192,7 @@ export class DebugImagesEffects {
setDebugImageIterations({min_iteration: res.min_iteration, max_iteration: res.max_iteration}),
setCurrentDebugImage({event: res.event}), deactivateLoader(action.type),
setDebugImageViewerScrollId({scrollId: res.scroll_id}),
action.navigateEarlier ? setViewerBeginningOfTime({beginningOfTime: false}) : setViewerEndOfTime({endOfTime: false})
!action.navigateEarlier ? setViewerBeginningOfTime({beginningOfTime: false}) : setViewerEndOfTime({endOfTime: false})
];
}
}),

View File

@@ -30,21 +30,21 @@
<div [ngClass]="{'disabled': (beginningOfTime$| async)[experimentId]}"
(click)="nextBatch({task: experimentId, metric: metricSelect.value})"
class="al-icon al-ico-next-batch al-color light-grey-blue"
class="al-icon al-ico-next-batch al-color blue-300"
smTooltip="Older images"></div>
<b class="text-right">{{debugImages?.[experimentId]?.data.slice(-1)[0].iter}}</b>
<b class="text-right">{{debugImages?.[experimentId]?.data?.slice(-1)[0].iter}}</b>
<div class="al-icon al-ico-between al-color light-blue-grey"></div>
<b>{{debugImages?.[experimentId]?.data?.[0].iter}}</b>
<div [ngClass]="{'disabled': (timeIsNow$| async)[experimentId]}"
(click)="(!timeIsNow[experimentId]) && previousBatch({task: experimentId, metric: metricSelect.value})"
class="al-icon al-ico-prev-batch al-color light-grey-blue"
class="al-icon al-ico-prev-batch al-color blue-300"
smTooltip="Newer images"></div>
<div [ngClass]="{'disabled': (timeIsNow$| async)[experimentId] && !allowAutorefresh }"
(click)="(!(timeIsNow[experimentId] && !allowAutorefresh)) && backToNow({task: experimentId, metric: metricSelect.value})"
class="al-icon al-ico-back-to-top al-color light-grey-blue"
class="al-icon al-ico-back-to-top al-color blue-300"
smTooltip="Newest samples"></div>
</div>
</div>

View File

@@ -25,6 +25,8 @@
}
}
.navigator-container{
height: 60px;
&.active{
background: $blue-50;
}

View File

@@ -156,7 +156,6 @@ export class DebugImagesComponent implements OnInit, OnDestroy, OnChanges {
return acc;
}), {}))
).subscribe(debugImages => {
console.log(debugImages);
this.debugImages = debugImages;
if (debugImages === null) {
return;

View File

@@ -176,7 +176,7 @@ $extra-header-min-height: 50px;
width: 24px;
margin-left: auto;
cursor: pointer;
color: $light-grey-blue;
color: $blue-300;
&:hover {
color: $blue-400;

View File

@@ -58,7 +58,7 @@
[selectFilteredItems]="selectShowIdenticalHyperParams$ | async"
[selectedItemsListMapper]="selectedItemsListMapper"
selectedItemsListPrefix=""
[limitSelection]="10"
[limitSelection]="50"
(selectedItems)="selectedParamsChanged($event)"
(clearSelection)="clearSelection()"
>

View File

@@ -56,10 +56,10 @@
<div class="buttons">
<button type="submit"
(click)="applyChanges()"
class="btn btn-neon yes-button">
class="btn btn-neon yes-button" data-id="ApplyButton">
APPLY
</button>
<button (click)="closeDialog()" class="btn btn-outline-neon">
<button (click)="closeDialog()" class="btn btn-outline-neon" data-id ="CancelButton">
CANCEL
</button>
</div>

View File

@@ -14,8 +14,8 @@
<span class="dot-container">
<span #dot class="dot pallete-cursor"
[style.background-color]="experimentsColors[experiment.id]"
smChooseColor [colorButtonRef]="dot"
[defaultColor]="experimentsColors[getExperimentNameForColor(experiment)]"
[colorButtonRef]="dot"
[smChooseColor]="experimentsColors[getExperimentNameForColor(experiment)]"
[stringToColor]="getExperimentNameForColor(experiment)">
</span>
</span>

View File

@@ -8,7 +8,7 @@ import {Task} from '~/business-logic/model/tasks/task';
import {select} from 'd3-selection';
import {sortCol} from '@common/shared/utils/tableParamEncode';
import {Store} from '@ngrx/store';
import {Axis, Color, ColorScale} from 'plotly.js';
import {Axis, Color, ColorBar, ColorScale} from 'plotly.js';
import domtoimage from 'dom-to-image/dist/dom-to-image.min';
import {from} from 'rxjs';
@@ -28,10 +28,13 @@ interface Dimension extends Partial<Axis> {
interface ParaPlotData {
type: string;
labelangle: 'auto' | number;
labelside?: 'top' | 'bottom';
dimensions: Dimension[];
line: {
color: Color;
colorscale?: ColorScale;
colorbar?: Partial<ColorBar>;
};
}
@@ -185,6 +188,7 @@ export class ParallelCoordinatesGraphComponent extends PlotlyGraphBaseComponent
if (this.parameters && filteredExperiments.length > 0) {
const trace = {
type: 'parcoords',
labelangle: 30,
dimensions: this.parameters.map((parameter) => {
parameter = `${parameter}.value`;
const allValuesIncludingNull = this.experiments.map(experiment => get(parameter, experiment.hyperparams));
@@ -209,7 +213,8 @@ export class ParallelCoordinatesGraphComponent extends PlotlyGraphBaseComponent
tickvals,
values: filteredExperiments.map((experiment) => (textVal[['', undefined].includes(get(parameter, experiment.hyperparams)) ? 'N/A' : get(parameter, experiment.hyperparams)])),
range: [0, max(tickvals)],
constraintrange
constraintrange,
};
})
} as ParaPlotData ;

View File

@@ -125,9 +125,11 @@ export const toggleSettings = createAction(EXPERIMENTS_OUTPUT_PREFIX + 'TOGGLE_S
export const getPlotSample = createAction(
EXPERIMENTS_OUTPUT_PREFIX + 'GET_PLOT_FOR_ITERATION',
props<{ task: string; metric: string; variant: string; iteration: number }>()
props<{ task: string; metric: string; iteration: number }>()
);
export const getNextPlotSample = createAction(EXPERIMENTS_OUTPUT_PREFIX + 'GET_NEXT_PLOT', props<{ task: string; navigateEarlier: boolean }>());
export const getNextPlotSample = createAction(
EXPERIMENTS_OUTPUT_PREFIX + 'GET_NEXT_PLOT',
props<{ task: string; navigateEarlier: boolean; iteration?: boolean }>());
export const setCurrentPlot = createAction(EXPERIMENTS_OUTPUT_PREFIX + 'SET_PLOT_FOR_ITERATION', props<{ event: any }>());
export const setPlotViewerScrollId = createAction(EXPERIMENTS_OUTPUT_PREFIX + 'SET_PLOT_VIEWER_SCROLL_ID', props<{ scrollId: string }>());
export const setPlotIterations = createAction(EXPERIMENTS_OUTPUT_PREFIX + 'SET_PLOT_ITERATIONS', props<PlotSampleResponse>());

View File

@@ -1,4 +1,4 @@
import {Action, createAction, props} from '@ngrx/store';
import {createAction, props} from '@ngrx/store';
import {IExperimentInfo, ISelectedExperiment} from '~/features/experiments/shared/experiment-info.model';
import {Project} from '~/business-logic/model/projects/project';
import {Queue} from '~/business-logic/model/queues/queue';
@@ -8,9 +8,6 @@ import { PipelinesStartPipelineRequest } from '~/business-logic/model/pipelines/
export const EXPERIMENTS_INFO_PREFIX = 'EXPERIMENTS_INFO_';
// EVENTS:
export const CLONE_EXPERIMENT_CLICKED = EXPERIMENTS_INFO_PREFIX + 'CLONE_EXPERIMENT_CLICKED';
export const publishClicked = createAction(
EXPERIMENTS_INFO_PREFIX + '[publish experiments]',
@@ -28,7 +25,7 @@ export const startPipeline = createAction(
export const getControllerForStartPipelineDialog = createAction(
EXPERIMENTS_INFO_PREFIX + '[Get Controller For Start Pipeline]',
props<{task:string}>()
props<{task: string}>()
);
export const setControllerForStartPipelineDialog = createAction(
@@ -47,12 +44,10 @@ export const dequeueClicked = createAction(
props<{ selectedEntities: ISelectedExperiment[] }>()
);
export class CloneExperimentClicked implements Action {
readonly type = CLONE_EXPERIMENT_CLICKED;
constructor(public payload: { originExperiment: ISelectedExperiment; cloneData: CloneExperimentPayload }) {
}
}
export const cloneExperimentClicked = createAction(
EXPERIMENTS_INFO_PREFIX + 'CLONE_EXPERIMENT_CLICKED',
props<{ originExperiment: ISelectedExperiment; cloneData: CloneExperimentPayload }>()
);
export const addTag = createAction(
EXPERIMENTS_INFO_PREFIX + '[add tag to experiment]',
@@ -85,7 +80,7 @@ export const navigateToQueue = createAction(
export const enqueueClicked = createAction(
EXPERIMENTS_INFO_PREFIX + '[enqueue experiments]',
props<{ selectedEntities: ISelectedExperiment[]; queue: Queue }>()
props<{ selectedEntities: ISelectedExperiment[]; queue: Queue; verifyWatchers: boolean }>()
);
export const archiveSelectedExperiments = createAction(

View File

@@ -81,7 +81,7 @@ export const setSelectedExperiment = createAction(
export const experimentSelectionChanged = createAction(
EXPERIMENTS_PREFIX + ' [experiment selection changed]',
props<{ experiment: { id?: string }; project?: string }>()
props<{ experiment: { id?: string }; project?: string; replaceURL?: boolean }>()
);

View File

@@ -34,7 +34,7 @@
[isGroupGraphs]="true"
[hiddenList]="listOfHidden | async"
[metrics]="graphs"
[legendStringLength]="minimized? 14 : undefined"
[legendStringLength]="minimized ? 14 : 19"
[minimized]="minimized"
[splitSize]="splitSize$ | async"
[isDarkTheme]="dark"

View File

@@ -59,7 +59,7 @@
[experimentName]="experimentName"
[hiddenList]="listOfHidden | async"
[smoothWeight]="smoothWeight$ | async"
[legendStringLength]="minimized? 14 : undefined"
[legendStringLength]="minimized ? 14 : 19"
[minimized]="minimized"
[xAxisType]="xAxisType$ | async"
[groupBy]="groupBy"

View File

@@ -1,7 +1,6 @@
import {Component, OnDestroy} from '@angular/core';
import {Component} from '@angular/core';
import {Store} from '@ngrx/store';
import {filter, map, take} from 'rxjs/operators';
import {Subscription} from 'rxjs';
import {selectSignedUrl} from '../../core/reducers/common-auth-reducer';
import {AdminService} from '~/shared/services/admin.service';
import {getSignedUrl} from '../../core/actions/common-auth.actions';

View File

@@ -1,6 +1,7 @@
<sm-menu
[iconClass]="'al-icon al-ico-settings al-color pointer create-new-icon ' + (disabled ? 'pointer-events-none blue-600' : 'blue-400')"
[iconClass]="'al-icon al-ico-settings al-color pointer create-new-icon ' + (disabled ? 'pointer-events-none blue-600' : 'blue-300')"
smMenuClass="light-theme custom-columns"
data-id="CustomizeColumn"
buttonTooltip="Customize table"
[showButton]="false"
(click)="!disabled && getMetricsToDisplay.emit()"
@@ -23,13 +24,13 @@
smClickStopPropagation
[ngClass]="{disabled: !metricVariants.length}"
(click)="$event.stopPropagation(); metricVariants.length && setMode(CustomColumnMode.Metrics)"
><i class="al-icon al-ico-add sm mr-1"></i><span class="caption">METRIC</span>
><i class="al-icon al-ico-add sm mr-1"></i><span data-id="Metric Button" class="caption">METRIC</span>
</div>
<div class="add-button metrics-button"
smClickStopPropagation
[ngClass]="{disabled: !hasHyperParams}"
(click)="$event.stopPropagation(); hasHyperParams && setMode(CustomColumnMode.HyperParams)"
><i class="al-icon al-ico-add sm mr-1"></i><span class="caption">HYPER PARAMETERS</span>
><i class="al-icon al-ico-add sm mr-1"></i><span data-id="Hyper Parameters Button" class="caption">HYPER PARAMETERS</span>
</div>
</div>
</div>
@@ -41,6 +42,7 @@
(selectedMetricToShow)="selectedMetricToShow.emit($event)">
</sm-select-metric-for-custom-col>
<sm-select-hyper-params-for-custom-col *ngIf="customColumnMode === CustomColumnMode.HyperParams"
class="hyper-params-custom-col"
[tableCols]="tableCols"
[hyperParams]="hyperParams"
(goBack)="setMode(CustomColumnMode.Standard)"

View File

@@ -9,25 +9,26 @@
.custom-column-buttons {
display: flex;
border-top: 1px solid #C3CDEF;
font-weight: 500;
color: $light-grey-blue;
color: $blue-400;
background: $faint-gray;
cursor: pointer;
div:not(.disabled):hover {
color: $blue-400;
color: $blue-300;
transition: color 0.3s;
}
.metrics-button {
width: 50%;
white-space: nowrap;
border-right: 1px solid #C3CDEF;
.caption {
margin-top: 2px;
}
&:first-child {
border-right: 1px solid $blue-200;
}
}
}
@@ -60,7 +61,6 @@ sm-select-metric-for-custom-col {
display: block;
height: 640px;
max-height: calc(100vh - 120px);
padding: 12px;
}
.custom-columns-disabled {

View File

@@ -39,7 +39,7 @@
matInput/>
</mat-form-field>
<div class="remove-step">
<i (click)="removeRow(index)" class="al-icon al-ico-trash al-color blue-300 sm-md pointer flashing-icon mb-2"></i>
<i (click)="removeRow(index)" class="al-icon al-ico-trash al-color blue-400 sm-md pointer flashing-icon mb-2"></i>
</div>
</div>
</ng-container>

View File

@@ -68,7 +68,7 @@
[numSelected]="1"
(tagSelected)="addTag($event)"
></sm-experiment-menu-extended>
<div *ngIf="minimized" (click)="closeInfoClicked.emit()" class="d-flex align-items-center line-item">
<div *ngIf="minimized" (click)="closeInfoClicked.emit()" class="d-flex align-items-center line-item" data-id="Cross Button">
<i class="al-icon al-ico-dialog-x pointer"></i>
</div>
</div>

View File

@@ -15,8 +15,8 @@
<sm-id-badge *ngIf="model?.id" class="ml-3" [id]="model.id"></sm-id-badge>
</div>
<ng-container *ngIf="editable">
<i class="al-icon al-ico-edit al-color blue-300 sm-md pointer mx-3" smTooltip="Select model" (click)="chooseModel()"></i>
<i *ngIf="model?.id" class="al-icon al-ico-trash al-color blue-300 sm-md pointer" smTooltip="Remove model" (click)="removeModel()"></i>
<i class="al-icon al-ico-edit al-color blue-400 sm-md pointer mx-3" smTooltip="Select model" (click)="chooseModel()"></i>
<i *ngIf="model?.id" class="al-icon al-ico-trash al-color blue-400 sm-md pointer" smTooltip="Remove model" (click)="removeModel()"></i>
</ng-container>
</div>
</sm-labeled-row>

View File

@@ -55,13 +55,13 @@
(formDataChanged)="headerCheckboxClicked()"
inputClassName="table-check-box select-col-checkbox"
></sm-checkbox-control>
<div class="al-icon al-ico-dropdown-arrow sm drop-down" [matMenuTriggerFor]="selectionMenu"></div>
<div class="al-icon al-ico-dropdown-arrow sm drop-down" data-id="Checkbox Dropdown" [matMenuTriggerFor]="selectionMenu"></div>
</div>
<mat-menu class="light-theme" #selectionMenu="matMenu">
<div *ngIf="entityType === entityTypes.experiment" class="menu-title">Select from project</div>
<button mat-menu-item (click)="selectAll()">All</button>
<button mat-menu-item (click)="emitSelection([])">None</button>
<button mat-menu-item (click)="selectAll(true)">All matching filter</button>
<button mat-menu-item data-id="All Option" (click)="selectAll()">All</button>
<button mat-menu-item data-id="None Option" (click)="emitSelection([])">None</button>
<button mat-menu-item data-id="All Matching Filter Option" (click)="selectAll(true)">All matching filter</button>
</mat-menu>
</ng-template>
<!--FILTER TEMPLATE-->

View File

@@ -1,11 +1,11 @@
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef,} from '@angular/core';
import {ChangeDetectionStrategy, ChangeDetectorRef, Component, EventEmitter, Input, OnDestroy, OnInit, Output, TemplateRef} from '@angular/core';
import {TIME_FORMAT_STRING} from '@common/constants';
import {ColHeaderTypeEnum, ISmCol} from '@common/shared/ui-components/data/table/table.consts';
import {FILTERED_EXPERIMENTS_STATUS_OPTIONS} from '../../shared/common-experiments.const';
import {get, uniq} from 'lodash/fp';
import {FilterMetadata} from 'primeng/api/filtermetadata';
import {ITableExperiment} from '../../shared/common-experiment-model.model';
import {EXPERIMENTS_TABLE_COL_FIELDS,} from '~/features/experiments/shared/experiments.const';
import {EXPERIMENTS_TABLE_COL_FIELDS} from '~/features/experiments/shared/experiments.const';
import {BaseTableView} from '@common/shared/ui-components/data/table/base-table-view';
import {getSystemTags, isDevelopment} from '~/features/experiments/shared/experiments.utils';
import {User} from '~/business-logic/model/users/user';
@@ -284,7 +284,7 @@ export class ExperimentsTableComponent extends BaseTableView implements OnInit,
if (!data?.single) {
this.contextExperiment = this._experiments.find(experiment => experiment.id === data.rowData.id);
if (!this.selectedExperiments.map(exp => exp.id).includes(this.contextExperiment.id)) {
this.prevSelected = this.contextExperiment;
this.prevSelected = this.contextExperiment.id;
this.emitSelection([this.contextExperiment]);
}
} else {

View File

@@ -1,8 +1,8 @@
<div class="modal-container">
<div class="viewer-header">
<div class="image-title third">
{{currentDebugImage?.metric? (currentDebugImage?.metric + ' - ' + currentDebugImage?.variant) : ''}}
<input #fakeInput name="Don't remove - it take the autofocus from slider" width="0" height="0" style="opacity: 0">
<h1 [smTooltip]="currentDebugImage?.metric? (currentDebugImage?.metric + ' - ' + currentDebugImage?.variant) : ''" smShowTooltipIfEllipsis>{{currentDebugImage?.metric? (currentDebugImage?.metric + ' - ' + currentDebugImage?.variant) : ''}}</h1>
<input #fakeInput name="Don't remove - it take the autofocus from slider" class="invisible">
</div>
<div class="third viewer-iteration">
<div *ngIf="minMaxIterations$| async as minMaxIterations">
@@ -24,10 +24,10 @@
<div class="d-flex third">
<div (click)="downloadImage()" smTooltip="Download" class="download icon-container"
[style.visibility]="(url | isVideo) ? 'hidden' : 'visible'">
<i class="al-icon al-ico-download al-color light-grey-blue"></i>
<i class="al-icon al-ico-download al-color blue-400"></i>
</div>
<div (click)="closeImageViewer()" smTooltip="Close" class="icon-container">
<i class="al-icon al-ico-dialog-x al-color blue-400 light-grey-blue"></i>
<i class="al-icon al-ico-dialog-x al-color blue-400"></i>
</div>
</div>
</div>
@@ -52,21 +52,21 @@
class="fit-to-screen d-flex flex-column justify-content-around align-items-center controller icon-container"
smTooltip="Fit to window" matTooltipPosition="left"
[class.disabled]="scale===autoFitScale">
<i class="al-icon al-ico-fit al-color light-grey-blue"></i>
<i class="al-icon al-ico-fit al-color blue-400"></i>
</div>
<div class="zoom controller d-flex flex-column justify-content-around align-items-center">
<div class="icon-container">
<i class="al-icon al-ico-plus al-color light-grey-blue" (click)="calculateNewScale(true)" smTooltip="Zoom in"
<i class="al-icon al-ico-plus al-color blue-400" (click)="calculateNewScale(true)" smTooltip="Zoom in"
matTooltipPosition="left"></i>
</div>
<div class="d-flex flex-column justify-content-around align-items-center icon-container" (click)="resetScale()"
smTooltip="Zoom to 1:1" matTooltipPosition="left"
[class.disabled]="scale===1">
<i class="al-icon al-ico-zoom-1-to-1 al-color light-grey-blue sm-md"></i>
<i class="al-icon al-ico-zoom-1-to-1 al-color blue-400 sm-md"></i>
</div>
<div class="icon-container">
<div class="al-icon al-ico-minus al-color light-grey-blue" (click)="calculateNewScale(false)"
<div class="al-icon al-ico-minus al-color blue-400" (click)="calculateNewScale(false)"
smTooltip="Zoom out" matTooltipPosition="left"></div>
</div>
</div>
@@ -74,10 +74,10 @@
<div class="navigation">
<div class="icon-container controller d-flex flex-column justify-content-around align-items-center next"
(click)="canGoNext() && next()" [class.disabled]="endOfTime$ | async">
<i class="al-icon al-ico-next al-color light-grey-blue"></i></div>
<i class="al-icon al-ico-next al-color blue-400"></i></div>
<div class="icon-container controller d-flex flex-column justify-content-around align-items-center previous"
(click)="canGoBack() && previous()" [class.disabled]="beginningOfTime$ | async">
<i class="al-icon al-ico-previous al-color light-grey-blue"></i></div>
<i class="al-icon al-ico-previous al-color blue-400"></i></div>
</div>
</div>

View File

@@ -9,6 +9,7 @@
display: flex;
align-items: center;
justify-content: center;
gap: 0 24px;
width: 100%;
height: 58px;
background-color: white;
@@ -24,22 +25,38 @@
align-items: center;
justify-content: center;
color: $blue-400;
font-size: 18px;
line-height: 1.5;
font-size: 16px;
.number {
color: $blue-500;
}
mat-slider.mat-slider-horizontal {
min-width: 280px;
@media screen and (min-width: 960px) {
mat-slider.mat-slider-horizontal {
min-width: 280px;
}
}
}
.image-title {
font-size: 18px;
color: $blue-500;
h1 {
font-size: 16px;
font-weight: 400;
color: $blue-500;
white-space: nowrap;
text-overflow: ellipsis;
overflow: hidden;
padding:0;
margin:0;
}
.invisible {
position: absolute;
width:0;
height:0;
padding: 0;
opacity: 0;
}
}
.download {

View File

@@ -90,6 +90,12 @@ export class ImageViewerComponent implements OnInit, OnDestroy {
case 'ArrowLeft':
this.previous();
break;
case 'ArrowUp':
this.nextIteration();
break;
case 'ArrowDown':
this.previousIteration();
break;
case '+':
this.calculateNewScale(true);
break;
@@ -197,6 +203,20 @@ export class ImageViewerComponent implements OnInit, OnDestroy {
}
}
private nextIteration() {
if (this.canGoNext() && this.currentDebugImage) {
this.imageLoaded = false;
this.store.dispatch(getNextDebugImageSample({task: this.currentDebugImage.task, navigateEarlier: false, iteration: true}));
}
}
previousIteration() {
if (this.canGoBack() && this.currentDebugImage) {
this.imageLoaded = false;
this.store.dispatch(getNextDebugImageSample({task: this.currentDebugImage.task, navigateEarlier: true, iteration: true}));
}
}
closeImageViewer() {
this.store.dispatch(resetViewer());
this.dialogRef.close();
@@ -315,4 +335,5 @@ export class ImageViewerComponent implements OnInit, OnDestroy {
showImage() {
this.imageLoaded = true;
}
}

View File

@@ -1,9 +1,7 @@
<div smClickStopPropagation class="d-flex justify-content-between hyper-params-container">
<i (click)="goBack.emit()" class="al-icon sm-md al-ico-back pointer" smClickStopPropagation></i>
<div class="hyper-param-header">SELECT HYPER PARAMETERS TO DISPLAY
</div>
<div smClickStopPropagation class="head">
<i (click)="goBack.emit()" data-id="Back Button" class="al-icon sm-md al-ico-back pointer m-auto" smClickStopPropagation></i>
<h3>SELECT HYPER PARAMETERS TO DISPLAY</h3>
</div>
<hr style="margin: 0">
<sm-grouped-checked-filter-list
smClickStopPropagation
[itemsList]="hyperParams"

View File

@@ -1,25 +1,31 @@
@import "../../../shared/ui-components/styles/variables";
@import "variables";
:host {
display: block;
height: 640px;
max-height: calc(100vh - 120px);
width: 370px;
}
.head {
margin-bottom: 12px;
padding: 12px;
display: grid;
grid-template-columns: 20px 1fr 20px;
gap: 6px;
align-items: center;
text-align: center;
border-bottom: 1px solid $blue-200;
h3 {
color: $blue-400;
font-size: 14px;
font-weight: normal;
margin: 0;
}
}
sm-grouped-checked-filter-list {
height: calc(100% - 52px);
padding: 15px 0 0 15px;
height: calc(100% - 58px);
::ng-deep .actions {
padding-top: 10px;
}
}
.hyper-param-header {
color: $blue-400;
flex-basis: 84%
}
.hyper-params-container {
padding: 15px
}

View File

@@ -1,9 +1,10 @@
<div smClickStopPropagation class="w-100 h-100">
<div *ngIf="goBack.observed" class="d-flex justify-content-between p-3">
<i (click)="goBack.emit()" class="al-icon sm-md al-ico-back pointer" smClickStopPropagation></i>
<div class="title">SELECT METRIC TO DISPLAY
</div>
<div smClickStopPropagation class="d-flex flex-column w-100 h-100">
<div *ngIf="goBack.observed" class="head">
<i (click)="goBack.emit()" data-id="Back button" class="al-icon sm-md al-ico-back pointer m-auto" smClickStopPropagation></i>
<h3>SELECT METRIC TO DISPLAY</h3>
</div>
<sm-search
class="underline-search"
[value]="searchText"

View File

@@ -1,9 +1,26 @@
@import "variables";
:host {
.title {
color: $blue-400;
flex-basis: 84%;
.head {
margin-bottom: 12px;
padding: 12px;
display: grid;
grid-template-columns: 20px 1fr 20px;
gap: 6px;
align-items: center;
text-align: center;
border-bottom: 1px solid $blue-200;
h3 {
color: $blue-400;
font-size: 14px;
font-weight: normal;
margin: 0;
}
}
sm-search {
margin: 0 12px;
}
.variant-label {
@@ -63,12 +80,13 @@
}
.metrics {
height: calc(100% - 32px);
height: calc(100% - 34px);
width: 100%;
overflow-y: scroll;
padding-left: 12px;
&.has-title {
height: calc(100% - #{32px + 49px});
// height: calc(100% - #{90px});
}
}

View File

@@ -27,6 +27,7 @@ import {ScalarKeyEnum} from '~/business-logic/model/events/scalarKeyEnum';
import {EMPTY} from 'rxjs';
import {selectSelectedExperiment} from '~/features/experiments/reducers';
import {IExperimentInfo} from '~/features/experiments/shared/experiment-info.model';
import {PlotSampleResponse} from '~/business-logic/model/events/plotSampleResponse';
@Injectable()
@@ -212,16 +213,15 @@ export class CommonExperimentOutputEffects {
task: action.task,
iteration: action.iteration,
metric: action.metric,
variant: action.variant,
scroll_id: scrollId,
navigate_current_metric: false
/* eslint-enable @typescript-eslint/naming-convention */
})
.pipe(
mergeMap(res => [
mergeMap((res: PlotSampleResponse) => [
// eslint-disable-next-line @typescript-eslint/naming-convention
outputActions.setPlotIterations({min_iteration: res.min_iteration, max_iteration: res.max_iteration}),
outputActions.setCurrentPlot({event: res.event}), deactivateLoader(action.type),
outputActions.setCurrentPlot({event: res.events}), deactivateLoader(action.type),
outputActions.setPlotViewerScrollId({scrollId: res.scroll_id}),
]),
catchError(error => [requestFailed(error), deactivateLoader(action.type)])
@@ -237,20 +237,21 @@ export class CommonExperimentOutputEffects {
/* eslint-disable @typescript-eslint/naming-convention */
task: action.task,
scroll_id: scrollId,
navigate_earlier: action.navigateEarlier
navigate_earlier: action.navigateEarlier,
...(action.iteration && {next_iteration: true})
/* eslint-enable @typescript-eslint/naming-convention */
})
.pipe(
mergeMap(res => {
if (!res.event) {
if (res.events.length === 0) {
return [action.navigateEarlier ? outputActions.setViewerBeginningOfTime({beginningOfTime: true}) : outputActions.setViewerEndOfTime({endOfTime: true})];
} else {
return [
// eslint-disable-next-line @typescript-eslint/naming-convention
outputActions.setPlotIterations({min_iteration: res.min_iteration, max_iteration: res.max_iteration}),
outputActions.setCurrentPlot({event: res.event}), deactivateLoader(action.type),
outputActions.setCurrentPlot({event: res.events}), deactivateLoader(action.type),
outputActions.setPlotViewerScrollId({scrollId: res.scroll_id}),
action.navigateEarlier ? outputActions.setViewerBeginningOfTime({beginningOfTime: false}) : outputActions.setViewerEndOfTime({endOfTime: false})
!action.navigateEarlier ? outputActions.setViewerBeginningOfTime({beginningOfTime: false}) : outputActions.setViewerEndOfTime({endOfTime: false})
];
}
}),

View File

@@ -64,6 +64,7 @@ import {PIPELINE_INFO_ONLY_FIELDS} from '@common/pipelines-controller/controller
import {TasksGetByIdExResponse} from '~/business-logic/model/tasks/tasksGetByIdExResponse';
import {ARTIFACTS_ONLY_FIELDS} from '@common/experiments/experiment.consts';
import {ITask} from '~/business-logic/model/al-task';
import {getTags} from '@common/core/actions/projects.actions';
@Injectable()
@@ -344,10 +345,11 @@ export class CommonExperimentsInfoEffects {
return [
commonInfoActions.experimentUpdatedSuccessfully({id: action.id}),
updateExperiment({id: action.id, changes}),
selectedExperiment?.id === action.id ? commonInfoActions.updateExperimentInfoData({
id: action.id,
changes
}) : new EmptyAction()
...(selectedExperiment?.id === action.id ?
[commonInfoActions.updateExperimentInfoData({ id: action.id, changes })] :
[]
),
...(changes.tags ? [getTags()] : [])
];
}),
catchError((err: HttpErrorResponse) => [

View File

@@ -7,7 +7,7 @@ import {BlTasksService} from '~/business-logic/services/tasks.service';
import {ApiEventsService} from '~/business-logic/api-services/events.service';
import {Router} from '@angular/router';
import {catchError, map, mergeMap, switchMap, tap, withLatestFrom} from 'rxjs/operators';
import {activeLoader, addMessage, deactivateLoader, setServerError} from '../../core/actions/layout.actions';
import {activeLoader, addMessage, deactivateLoader, neverShowPopupAgain, setServerError} from '../../core/actions/layout.actions';
import * as menuActions from '../actions/common-experiments-menu.actions';
import {stopClicked} from '../actions/common-experiments-menu.actions';
import {Observable, of} from 'rxjs';
@@ -45,6 +45,8 @@ import {get} from 'lodash/fp';
import {TaskTypeEnum} from '~/business-logic/model/tasks/taskTypeEnum';
import {TaskStatusEnum} from '~/business-logic/model/tasks/taskStatusEnum';
import {TasksCloneRequest} from '~/business-logic/model/tasks/tasksCloneRequest';
import {WelcomeMessageComponent} from '@common/layout/welcome-message/welcome-message.component';
import {selectNeverShowPopups} from '@common/core/reducers/view.reducer';
export const getChildrenExperiments = (tasksApi, parents, filters?: { [key: string]: any }): Observable<Task[]> =>
tasksApi.tasksGetAllEx({
@@ -101,11 +103,27 @@ export class CommonExperimentsMenuEffects {
/* eslint-disable @typescript-eslint/naming-convention */
ids,
queue: action.queue.id, ...((!action.queue.id) && {queue_name: action.queue.name}),
validate_tasks: true
validate_tasks: true,
...( action.verifyWatchers && {verify_watched_queue: true})
/* eslint-enable @typescript-eslint/naming-convention */
})
.pipe(
mergeMap(res => this.updateExperimentsSuccess(action, MenuItems.enqueue, ids, selectedEntity, res)),
withLatestFrom(this.store.select(selectNeverShowPopups)),
tap(([res, neverShowAgainPopups]) => {
if (res.queue_watched === false && !neverShowAgainPopups.includes('orphanedQueue')) {
this.dialog.open(WelcomeMessageComponent, {
data: {
queue: res.queue,
step: 2
}
}).afterClosed().subscribe(doNotShowAgain => {
if (doNotShowAgain) {
this.store.dispatch(neverShowPopupAgain({popupId: 'orphanedQueue'}));
}
});
}
}),
mergeMap(([res]) => this.updateExperimentsSuccess(action, MenuItems.enqueue, ids, selectedEntity, res)),
catchError(error => this.updateExperimentFailed(action.type, error))
);
}
@@ -182,32 +200,26 @@ export class CommonExperimentsMenuEffects {
cloneExperimentRequested$ = createEffect(() => this.actions$.pipe(
ofType<menuActions.CloneExperimentClicked>(menuActions.CLONE_EXPERIMENT_CLICKED),
ofType(menuActions.cloneExperimentClicked),
withLatestFrom(this.store.select(selectTableMode)),
switchMap(([action, tableMode]) => this.apiTasks.tasksClone({
task: action.payload.originExperiment.id,
switchMap(([action, ]) => this.apiTasks.tasksClone({
task: action.originExperiment.id,
/* eslint-disable @typescript-eslint/naming-convention */
new_task_project: action.payload.cloneData.project,
new_task_comment: action.payload.cloneData.comment,
new_task_name: action.payload.cloneData.name,
new_project_name: action.payload.cloneData.newProjectName
new_task_project: action.cloneData.project,
new_task_comment: action.cloneData.comment,
new_task_name: action.cloneData.name,
new_project_name: action.cloneData.newProjectName
/* eslint-enable @typescript-eslint/naming-convention */
} as TasksCloneRequest)
.pipe(
mergeMap(res => [
viewActions.getExperiments(),
viewActions.setSelectedExperiments({
experiments: (tableMode === 'info' ? [] :
[{id: res?.id}
])
}),
viewActions.experimentSelectionChanged({
experiment: tableMode === 'info'? {id: res.id}: null ,
project: action.payload.cloneData.project ? action.payload.cloneData.project : res?.new_project?.id
}),
deactivateLoader(action.type),
...action.payload.cloneData.newProjectName ? [getAllSystemProjects()] : [],
]),
mergeMap(res => {
this.router.navigate(['projects', action.cloneData.project, 'experiments', res.id]);
return [
viewActions.getExperiments(),
deactivateLoader(action.type),
...action.cloneData.newProjectName ? [getAllSystemProjects()] : [],
];
}),
catchError(error => [
deactivateLoader(action.type),
setServerError(error, null, 'Clone Experiment failed'),

View File

@@ -176,7 +176,7 @@ export class CommonExperimentsViewEffects {
lockRefresh = false;
refreshExperiments = createEffect(() => this.actions$.pipe(
ofType<ReturnType<typeof exActions.refreshExperiments>>(exActions.refreshExperiments),
ofType(exActions.refreshExperiments),
filter(() => !this.lockRefresh),
withLatestFrom(
this.store.select(exSelectors.selectCurrentScrollId),
@@ -232,7 +232,7 @@ export class CommonExperimentsViewEffects {
return [
requestFailed(error),
deactivateLoader(action.type),
addMessage('warn', 'Fetch Experiments failed', error?.meta && [{name: 'More info', actions: [setServerError(error, null, 'Fetch Experiments failed')]}])
...(action.autoRefresh ? [] : [addMessage('warn', 'Fetch Experiments failed', error?.meta && [{name: 'More info', actions: [setServerError(error, null, 'Fetch Experiments failed')]}])])
];
})
);
@@ -281,7 +281,8 @@ export class CommonExperimentsViewEffects {
experimentSelectionChanged = createEffect(() => this.actions$.pipe(
ofType(exActions.experimentSelectionChanged),
withLatestFrom(this.store.select(selectRouterConfig)),
tap(([action, routeConfig]) => this.navigateAfterExperimentSelectionChanged(action.experiment as ITableExperiment, action.project, routeConfig)),
tap(([action, routeConfig]) =>
this.navigateAfterExperimentSelectionChanged(action.experiment as ITableExperiment, action.project, routeConfig, action.replaceURL)),
mergeMap(() => [exActions.setTableMode({mode: 'info'})])
));
@@ -518,7 +519,7 @@ export class CommonExperimentsViewEffects {
})
));
navigateAfterExperimentSelectionChanged(selectedExperiment: ITableExperiment, experimentProject: string, routeConfig: string[]) {
navigateAfterExperimentSelectionChanged(selectedExperiment: ITableExperiment, experimentProject: string, routeConfig: string[], replaceUrl=false) {
const module = routeConfig.includes('datasets') ? 'datasets/simple' : routeConfig.includes('pipelines') ? 'pipelines' : 'projects';
// wow angular really suck...
const activeChild = get('firstChild.firstChild.firstChild.firstChild.firstChild.firstChild', this.route);
@@ -528,7 +529,7 @@ export class CommonExperimentsViewEffects {
[module, experimentProject, 'experiments', selectedExperiment.id].concat(activeChild ? activeChildUrl.split('/') : []),
{queryParamsHandling: 'preserve'}
) :
this.router.navigate([module, experimentProject, 'experiments'], {queryParamsHandling: 'preserve'});
this.router.navigate([module, experimentProject, 'experiments'], {queryParamsHandling: 'preserve', replaceUrl});
}
getGetAllQuery({

View File

@@ -26,6 +26,7 @@
<button
class="btn btn-cml-primary d-flex justify-content-between align-items-center mr-3"
[disabled]="isArchived$ | async"
data-id="New Experiment"
(click)="newExperiment()"
[smTooltip]="isSmallScreen ? 'NEW EXPERIMENT' : ''"
>

View File

@@ -141,9 +141,9 @@ export class ExperimentsComponent extends BaseEntityPageComponent implements OnI
protected route: ActivatedRoute,
protected router: Router,
protected dialog: MatDialog,
protected refresh: RefreshService
protected refresh: RefreshService,
) {
super(store, route, router, dialog, refresh);
super(store, route, router, dialog, refresh, syncSelector);
this.selectSplitSize$ = this.store.select(selectSplitSize);
this.isPipeline$ = this.store.select(selectIsPipelines);
this.tableSortFields$ = this.store.select(selectTableSortFields).pipe(tap(field => this.sortFields = field));
@@ -352,7 +352,7 @@ export class ExperimentsComponent extends BaseEntityPageComponent implements OnI
this.compareExperiments();
break;
case MenuItems.archive:
this.contextMenu.restoreArchive();
this.contextMenu.restoreArchive(item.entitiesType);
break;
case MenuItems.reset:
this.contextMenu.resetPopup();

View File

@@ -11,10 +11,10 @@
</ng-template>
<div class="buttons">
<button (click)="closeDialog(true)" [disabled]="!(shouldBeAbortedTasks?.length>0)"
class="btn btn-neon yes-button">
class="btn btn-neon yes-button" data-id ="AbortButton">
ABORT
</button>
<button (click)="closeDialog(false)" class="btn btn-outline-neon">
<button (click)="closeDialog(false)" class="btn btn-outline-neon" data-id ="CancelButton">
CANCEL
</button>
</div>

View File

@@ -56,10 +56,10 @@
</ng-form>
<div class="buttons">
<button [disabled]="moveToForm.invalid" (click)="closeDialog(true)" cdkFocusInitial
class="btn btn-neon yes-button" #moveButton>
class="btn btn-neon yes-button" data-id="MoveButton" #moveButton>
MOVE
</button>
<button (click)="closeDialog(false)" class="btn btn-outline-neon">
<button (click)="closeDialog(false)" class="btn btn-outline-neon" data-id ="CancelButton">
CANCEL
</button>

View File

@@ -82,10 +82,10 @@
<div class="buttons">
<button cdkFocusInitial (click)="closeDialog(true)" [disabled]="!cloneForm.valid"
class="btn btn-neon yes-button" #cloneButton>
class="btn btn-neon yes-button" data-id="CloneButton" #cloneButton>
{{extend ? 'EXTEND' : 'CLONE'}}
</button>
<button (click)="closeDialog(false)" class="btn btn-outline-neon">
<button (click)="closeDialog(false)" class="btn btn-outline-neon" data-id ="CancelButton">
CANCEL
</button>

View File

@@ -1,13 +1,13 @@
import {MAT_DIALOG_DATA, MatDialogRef} from '@angular/material/dialog';
import {Project} from '../../../../../business-logic/model/projects/project';
import {Project} from '~/business-logic/model/projects/project';
import {Component, ElementRef, Inject, OnDestroy, OnInit, ViewChild} from '@angular/core';
import {Store} from '@ngrx/store';
import {NgForm} from '@angular/forms';
import {Observable, Subscription} from 'rxjs';
import {selectRootProjects} from '../../../../core/reducers/projects.reducer';
import {getAllSystemProjects} from '../../../../core/actions/projects.actions';
import {selectRootProjects} from '@common/core/reducers/projects.reducer';
import {getAllSystemProjects} from '@common/core/actions/projects.actions';
import {map} from 'rxjs/operators';
import {isReadOnly} from '../../../../shared/utils/shared-utils';
import {isReadOnly} from '@common/shared/utils/shared-utils';
import {CloneForm} from '../../common-experiment-model.model';
import {isEqual} from 'lodash/fp';
@@ -18,7 +18,6 @@ import {isEqual} from 'lodash/fp';
})
export class CloneDialogComponent implements OnInit, OnDestroy {
CLONE_NAME_PREFIX;
public reference: string;
public header: string;
public type: string;
@@ -28,10 +27,11 @@ export class CloneDialogComponent implements OnInit, OnDestroy {
name: null,
comment: null
} as CloneForm;
public projects: { label: string; value: string }[];
private readonly defaultProjectId: string;
private projectsSub: Subscription;
public projects: { label: string; value: string }[];
private readonly cloneNamePrefix: string;
@ViewChild('cloneForm', {static: true}) cloneForm: NgForm;
@ViewChild('cloneButton', {static: true}) cloneButton: ElementRef;
@@ -54,20 +54,20 @@ export class CloneDialogComponent implements OnInit, OnDestroy {
public dialogRef: MatDialogRef<CloneDialogComponent>
) {
this.readOnlyProjects$ = this.store.select(selectRootProjects)
.pipe(map(projects => projects.filter(project => isReadOnly(project)).map(project => project.name)));
.pipe(map(projects => projects?.filter(project => isReadOnly(project)).map(project => project.name)));
this.projects$ = this.store.select(selectRootProjects)
.pipe(map(projects => projects.filter(project => !isReadOnly(project))));
.pipe(map(projects => projects?.filter(project => !isReadOnly(project))));
this.defaultProjectId = data.defaultProject;
this.header = `${data.extend ? 'Extend' : 'Clone'} ${data.type}`;
this.CLONE_NAME_PREFIX = data.extend ? '' : 'Clone Of ';
this.cloneNamePrefix = data.extend ? '' : 'Clone Of ';
this.type = data.type.toLowerCase();
this.reference = data.defaultName;
this.extend = data.extend;
this.formData.name = this.CLONE_NAME_PREFIX;
this.formData.name = this.cloneNamePrefix;
setTimeout(() => {
this.formData = {
...this.formData,
name: this.extend ? '' : this.CLONE_NAME_PREFIX + data.defaultName,
name: this.extend ? '' : this.cloneNamePrefix + data.defaultName,
comment: data.defaultComment || '',
};
});
@@ -81,7 +81,7 @@ export class CloneDialogComponent implements OnInit, OnDestroy {
ngOnInit(): void {
this.store.dispatch(getAllSystemProjects());
this.projectsSub = this.projects$.subscribe(projects => {
const projectList = projects.map(project => ({value: project.id, label: project.name}));
const projectList = projects?.map(project => ({value: project.id, label: project.name}));
if (!isEqual(projectList, this.projects)) {
this.projects = projectList;
const defaultProject = this.projects.find(project => project.value === this.defaultProjectId);

View File

@@ -9,26 +9,26 @@
</div>
<mat-menu #experimentMenu="matMenu" [hasBackdrop]="backdrop" classList="light-theme">
<ng-container *ngIf="selectedDisableAvailable[menuItems.queue]">
<button *ngIf="tableMode" mat-menu-item (click)="toggleDetails()">
<i [class]="'al-icon '+ ICONS.DETAILS + ' sm-md'"></i>Details
<button *ngIf="tableMode" mat-menu-item (click)="toggleDetails()" data-id="Details Option">
<i [class]="'al-icon '+ icons.DETAILS + ' sm-md'"></i>Details
</button>
<button mat-menu-item (click)="toggleFullScreen(minimizedView)" [disabled]="isSharedAndNotOwner">
<i [class]="'al-icon '+ ICONS.CHART + ' sm-md'"></i>
<button mat-menu-item (click)="toggleFullScreen(minimizedView)" [disabled]="isSharedAndNotOwner" data-id="View Full Screen Option">
<i [class]="'al-icon '+ icons.CHART + ' sm-md'"></i>
{{minimizedView ? 'View Full Screen' : 'View in Experiment Table'}}
</button>
<hr>
<button mat-menu-item (click)="manageQueueClicked()"
[disabled]="selectedDisableAvailable[menuItems.queue].disable">
<i [class]="'al-icon '+ ICONS.QUEUED + ' sm-md'"></i>
[disabled]="selectedDisableAvailable[menuItems.queue].disable" data-id="Manage Queue Option">
<i [class]="'al-icon '+ icons.QUEUED + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.queue]?.available | menuItemText : 'Manage Queue' : selectedDisableAvailableIsMultiple}}
</button>
<button mat-menu-item (click)="viewWorkerClicked()"
[disabled]="selectedDisableAvailable[menuItems.viewWorker].disable">
<i [class]="'al-icon '+ ICONS.WORKER + ' sm-md'"></i>
[disabled]="selectedDisableAvailable[menuItems.viewWorker].disable" data-id="View Worker Option">
<i [class]="'al-icon '+ icons.WORKER + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.viewWorker]?.available | menuItemText : 'View Worker' : selectedDisableAvailableIsMultiple}}
</button>
@@ -36,72 +36,72 @@
<button mat-menu-item (click)="shareExperimentPopup()" *ngIf="isCommunity"
[disabled]="isExample || numSelected > 1 || isArchive">
<i [class]="'al-icon ' + ICONS.SHARE + ' sm-md'"></i>Share
<i [class]="'al-icon ' + icons.SHARE + ' sm-md'"></i>Share
</button>
<button mat-menu-item (click)="deleteExperimentPopup()"
[disabled]="selectedDisableAvailable[menuItems.delete].disable" *ngIf="isArchive">
<i [class]="'al-icon ' + ICONS.REMOVE + ' sm-md'"></i>
[disabled]="selectedDisableAvailable[menuItems.delete].disable" *ngIf="isArchive" data-id="Delete Option">
<i [class]="'al-icon ' + icons.REMOVE + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.delete].available | menuItemText : 'Delete' : selectedDisableAvailableIsMultiple}}
</button>
<button mat-menu-item (click)="restoreArchive()"
<button mat-menu-item (click)="restoreArchive()" data-id="Archive Option"
[disabled]="selectedDisableAvailable[menuItems.archive].disable">
<i class="al-icon sm-md" [class]="isArchive ? ICONS.RESTORE : ICONS.ARCHIVE"></i>
<i class="al-icon sm-md" [class]="isArchive ? icons.RESTORE : icons.ARCHIVE"></i>
{{selectedDisableAvailable[menuItems.archive].available | menuItemText : isArchive ? 'Restore from Archive' : 'Archive' : selectedDisableAvailableIsMultiple}}
</button>
<button mat-menu-item (click)="enqueuePopup()"
<button mat-menu-item (click)="enqueuePopup()" data-id="Enqueue Option"
*ngIf="!selectedDisableAvailable[menuItems.enqueue].disable && !isArchive"
[disabled]="selectedDisableAvailable[menuItems.enqueue].disable">
<i [class]="'al-icon ' + ICONS.ENQUEUE + ' sm-md'"></i>
<i [class]="'al-icon ' + icons.ENQUEUE + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.enqueue]?.available | menuItemText : 'Enqueue' : selectedDisableAvailableIsMultiple}}
</button>
<button mat-menu-item (click)="dequeuePopup()"
<button mat-menu-item (click)="dequeuePopup()" data-id="Dequeue Option"
*ngIf="!selectedDisableAvailable[menuItems.dequeue].disable"
[disabled]="selectedDisableAvailable[menuItems.dequeue]?.disable">
<i [class]="'al-icon ' + ICONS.DEQUEUE + ' sm-md'"></i>
<i [class]="'al-icon ' + icons.DEQUEUE + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.dequeue]?.available | menuItemText : 'Dequeue' : selectedDisableAvailableIsMultiple}}
</button>
<button mat-menu-item (click)="resetPopup()" [disabled]="selectedDisableAvailable[menuItems.reset].disable">
<i [class]="'al-icon ' + ICONS.RESET + ' sm-md'"></i>
<button mat-menu-item (click)="resetPopup()" data-id="Reset Option" [disabled]="selectedDisableAvailable[menuItems.reset].disable">
<i [class]="'al-icon ' + icons.RESET + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.reset].available | menuItemText : 'Reset' : selectedDisableAvailableIsMultiple}}
</button>
<button mat-menu-item (click)="stopPopup()" [disabled]="selectedDisableAvailable[menuItems.abort].disable">
<i [class]="'al-icon ' + ICONS.STOPPED + ' sm-md'"></i>
<button mat-menu-item (click)="stopPopup()" data-id="Abort Option" [disabled]="selectedDisableAvailable[menuItems.abort].disable">
<i [class]="'al-icon ' + icons.STOPPED + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.abort].available | menuItemText : 'Abort' : selectedDisableAvailableIsMultiple }}
</button>
<button mat-menu-item (click)="stopAllChildrenPopup()" *ngIf="!selectedDisableAvailable[menuItems.abortAllChildren]?.disable">
<i [class]="'al-icon ' + ICONS.STOPPED_ALL + ' sm-md'"></i>
<i [class]="'al-icon ' + icons.STOPPED_ALL + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.abortAllChildren]?.available | menuItemText : 'Abort All Children' : selectedDisableAvailableIsMultiple }}
</button>
<button
mat-menu-item
(click)="publishPopup()"
(click)="publishPopup()" data-id="Publish Option"
[disabled]="selectedDisableAvailable[menuItems.publish].disable">
<i [class]="'al-icon ' + ICONS.PUBLISHED + ' sm-md'"></i>
<i [class]="'al-icon ' + icons.PUBLISHED + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.publish].available | menuItemText : 'Publish' : selectedDisableAvailableIsMultiple }}
</button>
<hr>
<button mat-menu-item
[matMenuTriggerFor]="tagMenu"
[matMenuTriggerFor]="tagMenu" data-id="Add Tag Option"
[disabled]="selectedDisableAvailable[menuItems.tags]?.disable"
(menuOpened)="tagMenuOpened()"
(menuClosed)="tagMenuClosed()">
<i [class]="'al-icon ' + ICONS.TAG + ' sm-md'">
<i [class]="'al-icon ' + icons.TAG + ' sm-md'">
</i><span>{{selectedDisableAvailable[menuItems.tags]?.available | menuItemText : 'Add Tag' : selectedDisableAvailableIsMultiple }}</span>
</button>
<hr>
<button mat-menu-item (click)="clonePopup()">
<i [class]="'al-icon ' + ICONS.CLONE + ' sm-md'"></i>Clone
<button mat-menu-item (click)="clonePopup()" data-id="Clone Option">
<i [class]="'al-icon ' + icons.CLONE + ' sm-md'"></i>Clone
</button>
<button mat-menu-item (click)="moveToProjectPopup()"
<button mat-menu-item (click)="moveToProjectPopup()" data-id="Move To Project Option"
[disabled]="selectedDisableAvailable[menuItems.moveTo].disable">
<i [class]="'al-icon ' + ICONS.MOVE_TO + ' sm-md'"></i>
<i [class]="'al-icon ' + icons.MOVE_TO + ' sm-md'"></i>
{{selectedDisableAvailable[menuItems.moveTo].available | menuItemText : 'Move to Project' : selectedDisableAvailableIsMultiple}}
</button>
<ng-content select="[extended]"></ng-content>

View File

@@ -2,7 +2,7 @@ import {MatDialog, MatDialogRef} from '@angular/material/dialog';
import {ActivatedRoute, Router} from '@angular/router';
import {Store} from '@ngrx/store';
import {get} from 'lodash/fp';
import {filter, take, withLatestFrom} from 'rxjs/operators';
import {filter, take} from 'rxjs/operators';
import {ICONS} from '@common/constants';
import {Queue} from '~/business-logic/model/queues/queue';
import {TaskStatusEnum} from '~/business-logic/model/tasks/taskStatusEnum';
@@ -42,9 +42,7 @@ import {
selectionDisabledPublishExperiments,
selectionDisabledReset
} from '@common/shared/entity-page/items.utils';
import {WelcomeMessageComponent} from '@common/layout/welcome-message/welcome-message.component';
import {resetOutput} from '@common/experiments/actions/common-experiment-output.actions';
import {updateManyExperiment} from '../../../actions/common-experiments-view.actions';
@Component({
@@ -53,16 +51,18 @@ import {updateManyExperiment} from '../../../actions/common-experiments-view.act
styleUrls: ['./experiment-menu.component.scss']
})
export class ExperimentMenuComponent extends BaseContextMenuComponent implements OnInit {
readonly ICONS = ICONS;
readonly TaskStatusEnum = TaskStatusEnum;
readonly TaskTypeEnum = TaskTypeEnum;
readonly icons = ICONS;
readonly taskStatusEnum = TaskStatusEnum;
readonly taskTypeEnum = TaskTypeEnum;
public open: boolean;
public isExample: boolean;
public isArchive: boolean;
public selectionHasExamples: boolean;
public isCommunity: boolean;
protected _experiment: ISelectedExperiment = null;
private _selectedExperiments: ISelectedExperiment[];
@Input() selectedExperiment: any;
@Input() isSharedAndNotOwner = false;
@Input() tagsFilterByProject: boolean;
@@ -77,8 +77,10 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
this.isArchive = experiment?.system_tags?.includes('archived');
}
get experiment() {
return this._experiment;
}
@Output() tagSelected = new EventEmitter<string>();
@Input() neverShowPopups;
@Input() minimizedView: boolean;
@@ -90,12 +92,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
get selectedExperiments(): ISelectedExperiment[] {
return this._selectedExperiments;
}
get experiment() {
return this._experiment;
}
public isCommunity: boolean;
@Output() tagSelected = new EventEmitter<string>();
constructor(
protected blTaskService: BlTasksService,
@@ -125,7 +122,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
const showShareWarningDialog = selectedExperiments.find(item => item?.system_tags.includes('shared')) &&
!this.syncSelector.selectSync(selectNeverShowPopups)?.includes('archive-shared-task');
if (showShareWarningDialog) {
this.showConfirmArchiveExperiments(this.store, this.dialog, selectedExperiments);
this.showConfirmArchiveExperiments(this.store, this.dialog, selectedExperiments, entityType);
} else {
this.store.dispatch(commonMenuActions.archiveSelectedExperiments({selectedEntities: selectedExperiments, entityType}));
}
@@ -154,23 +151,10 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
data: {taskIds: selectedExperiments.map(exp => exp.id), reference: selectedExperiments[0].name}
});
selectQueueDialog.afterClosed().pipe(withLatestFrom(this.store.select(selectNeverShowPopups))).subscribe(([res, neverShowAgainPopups]) => {
selectQueueDialog.afterClosed().subscribe(res => {
if (res && res.confirmed) {
this.enqueueExperiment(res.queue, selectedExperiments);
this.blTaskService.setPreviouslyUsedQueue(res.queue);
if (((!res.queue.workers) || res.queue.workers.length === 0) && (!neverShowAgainPopups.includes('orphanedQueue'))) {
const orphanedQueueDialog: MatDialogRef<WelcomeMessageComponent> = this.dialog.open(WelcomeMessageComponent, {
data: {
queue: res.queue,
step: 2
}
});
orphanedQueueDialog.afterClosed().subscribe((doNotShowAgain) => {
if (doNotShowAgain) {
this.store.dispatch(neverShowPopupAgain({popupId: 'orphanedQueue'}));
}
});
}
}
});
}
@@ -205,7 +189,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
}
private enqueueExperiment(queue, selectedExperiments) {
this.store.dispatch(commonMenuActions.enqueueClicked({selectedEntities: selectedExperiments, queue}));
this.store.dispatch(commonMenuActions.enqueueClicked({selectedEntities: selectedExperiments, queue, verifyWatchers: true}));
}
private dequeueExperiment(selectedExperiments) {
@@ -358,7 +342,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
}
cloneExperiment(cloneData) {
this.store.dispatch(new commonMenuActions.CloneExperimentClicked({
this.store.dispatch(commonMenuActions.cloneExperimentClicked({
originExperiment: this._experiment,
cloneData: {
...cloneData,
@@ -395,7 +379,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
});
}
showConfirmArchiveExperiments(store: Store, dialog: MatDialog, selectedExperiments: ISelectedExperiment[]): void {
showConfirmArchiveExperiments(store: Store, dialog: MatDialog, selectedExperiments: ISelectedExperiment[], entityType: EntityTypeEnum): void {
const confirmDialogRef = dialog.open(ConfirmDialogComponent, {
data: {
title: 'ARCHIVE A PUBLICLY SHARED TASK',
@@ -409,7 +393,7 @@ export class ExperimentMenuComponent extends BaseContextMenuComponent implements
});
confirmDialogRef.afterClosed().subscribe((confirmed) => {
if (confirmed) {
store.dispatch(archiveSelectedExperiments({selectedEntities: selectedExperiments}));
store.dispatch(archiveSelectedExperiments({selectedEntities: selectedExperiments, entityType}));
if (confirmed.neverShowAgain) {
store.dispatch(neverShowPopupAgain({popupId: 'archive-shared-task'}));
}

View File

@@ -59,7 +59,7 @@
::ng-deep .ui-state-highlight{
sm-experiment-status-icon-label, sm-experiment-type-icon-label {
.published, .created, .in_progress, .queued, .failed, .stopped, .training, .testing, .completed, .aborted{
color: $almost-white;
color: $blue-50;
filter: brightness(0) invert(1);
}
}
@@ -68,7 +68,7 @@
::ng-deep .selected {
sm-experiment-status-icon-label, sm-experiment-type-icon-label {
.published, .created, .in_progress, .queued, .failed, .stopped, .training, .testing, .completed, .aborted{
color: $almost-white;
color: $blue-50;
filter: brightness(0) invert(1);
}
}

View File

@@ -13,13 +13,16 @@
class="thin short"
floatLabel="always">
<mat-label class="field-label">Queue</mat-label>
<input type="text"
#text
tabindex="0"
matInput
smRequiredAutocompleteSelectionValidator
[formControl]="queueControl"
[matAutocomplete]="auto">
<input
type="text"
#text
data-id="Select Queue"
tabindex="0"
matInput
smRequiredAutocompleteSelectionValidator
[formControl]="queueControl"
[matAutocomplete]="auto"
>
<mat-autocomplete
class="light-theme"
[displayWith]="displayFn"
@@ -27,11 +30,11 @@
>
<mat-option
class="item"
*ngIf="(userAllowedToCreateQueue$ | async) &&(queueControl.value && !(displayFn(queueControl.value) | stringIncludedInArray:queuesNames))"
*ngIf="(userAllowedToCreateQueue$ | async) && queueControl.value && !(displayFn(queueControl.value) | stringIncludedInArray: queuesNames)"
[value]="{name:queueControl.value}"
>"{{displayFn(queueControl.value)}}"<span class="new">(Create New)</span></mat-option>
<mat-option
*ngFor="let option of filteredOptions | async; trackBy: trackById"
*ngFor="let option of filteredOptions$ | async; trackBy: trackById"
[value]="option"
[smTooltip]="option.name" smShowTooltipIfEllipsis
>
@@ -48,10 +51,10 @@
</form>
<div class="buttons">
<button (click)="closeDialog(false)" class="btn btn-outline-neon">
<button (click)="closeDialog(false)" class="btn btn-outline-neon" data-id ="CancelButton">
CANCEL
</button>
<button (click)="closeDialog(true)" class="btn btn-neon yes-button" [disabled]="!queueControl.valid">
<button (click)="closeDialog(true)" class="btn btn-neon yes-button" [disabled]="!queueControl.valid" data-id="EnqueueButton">
ENQUEUE
</button>
</div>

View File

@@ -7,7 +7,7 @@
p {
text-align: center;
font-size: 14px;
color: $dusk-two;
color: $blue-450;
overflow-wrap: break-word;
}

View File

@@ -7,7 +7,7 @@ import {Queue} from '~/business-logic/model/queues/queue';
import {ConfirmDialogComponent} from '@common/shared/ui-components/overlay/confirm-dialog/confirm-dialog.component';
import {BlTasksService} from '~/business-logic/services/tasks.service';
import {filter, map, startWith} from 'rxjs/operators';
import {Observable, Subscription} from 'rxjs';
import {combineLatest, Observable, Subscription} from 'rxjs';
import {userAllowedToCreateQueue$} from '~/core/reducers/users.reducer';
import {trackById} from '@common/shared/utils/forms-track-by';
import {FormControl} from '@angular/forms';
@@ -34,7 +34,8 @@ export class SelectQueueComponent implements OnInit, OnDestroy {
split = splitLine;
displayFn = (item: any): string => typeof item === 'string' ? item : item?.name;
trackById = trackById;
public filteredOptions: Observable<Queue[]>;
public filteredOptions$: Observable<Queue[]>;
defaultQueue: Queue;
constructor(
public dialogRef: MatDialogRef<ConfirmDialogComponent>,
@@ -56,8 +57,8 @@ export class SelectQueueComponent implements OnInit, OnDestroy {
if (queues) {
this.queues = queues;
this.queuesNames = queues.map(q => q.name);
const selectedQueue = this.blTaskService.getDefaultQueue(this.queues) || queues[0];
this.queueControl.setValue(selectedQueue, {emitEvent: false});
this.defaultQueue = this.blTaskService.getDefaultQueue(this.queues) || queues[0];
this.queueControl.reset(this.defaultQueue, {emitEvent: false});
this.cdr.detectChanges();
}
});
@@ -65,16 +66,22 @@ export class SelectQueueComponent implements OnInit, OnDestroy {
ngOnInit() {
this.store.dispatch(new GetQueuesForEnqueue());
this.filteredOptions = this.queueControl.valueChanges.pipe(
startWith(''),
map(value => {
if (!this.queues) {
return [];
}
const name = (typeof value === 'string' ? value : value?.name).toLowerCase();
return name ? this.queues.filter(q => q.name.toLowerCase().includes(name)) : this.queues.slice();
}),
);
this.filteredOptions$ = combineLatest([
this.queueControl.valueChanges.pipe(startWith('')),
this.queues$
])
.pipe(
map(([value, queues]) => {
if (!queues) {
return [];
}
const name = (typeof value === 'string' ? value : value?.name).toLowerCase();
if (this.queueControl.pristine || !name) {
return queues;
}
return queues.filter(q => q.name.toLowerCase().includes(name));
}),
);
}
closeDialog(confirmed) {

View File

@@ -70,9 +70,7 @@ export const prepareNames = (data: IBreadcrumbs, customProject?: boolean, fullSc
const project = prepareLinkData(data.project, true);
if (data.project) {
let subProjectsNames = [data.project?.name];
if (!customProject) {
subProjectsNames = data.project?.name?.split('/');
}
subProjectsNames = data.project?.name?.split('/').filter(name=> !['.datasets', '.pipelines'].includes(name));
const allProjects = [
...(data.projects || []),
{id: '*', name: 'All Experiments'},
@@ -81,13 +79,14 @@ export const prepareNames = (data: IBreadcrumbs, customProject?: boolean, fullSc
let currentName = '';
const subProjects = subProjectsNames.map(name => {
currentName += currentName ? ('/' + name) : name;
return allProjects.find(proj => currentName === proj.name);
return allProjects.find(proj => currentName === proj.name.replace('/.datasets', '').replace('/.pipelines', ''));
}) || [];
const subProjectsLinks = subProjects.map((subProject, index, arr) => ({
name: subProject?.name.substring(subProject?.name.lastIndexOf('/') + 1),
url: customProject ?
data.project?.system_tags?.includes('pipeline') ? `pipelines/${subProject?.id}/experiments` : `datasets/simple/${subProject?.id}` :
name: subProjectsNames[index],
url: customProject ? (( index===subProjects.length-1) ?
(data.project?.system_tags?.includes('pipeline') ?
`pipelines/${subProject?.id}/experiments` : `datasets/simple/${subProject?.id}`): null)
:
fullScreen && index === (arr.length - 1) ? `projects/${subProject?.id}/experiments/${data?.experiment?.id}` :
subProject?.name === data.project?.name && data.project?.sub_projects?.length === 0 ?
`projects/${subProject?.id}` :

View File

@@ -81,7 +81,7 @@
<div class="share-menu-container" smClickStopPropagation>
<div class="d-flex align-items-center justify-content-between top">
<div class="share-title">SHARE VIEW</div>
<i class="pointer al-icon al-ico-dialog-x al-color blue-400 sm-md close-dialog"
<i class="pointer al-icon al-ico-dialog-x al-color blue-300 sm-md close-dialog"
(click)="menuTrigger.closeMenu()" smClickStopPropagation></i>
</div>
<div class="copy-title">Copy the following URL to share this view with others</div>

View File

@@ -23,11 +23,11 @@
<div class="right-buttons">
<sm-common-search #search [class.share-view]="isShareMode"></sm-common-search>
<a class="pointer resources-trigger" [matMenuTriggerFor]="resourcesMenu">
<i class="far al-ico-help-outlined"></i>
<a class="d-flex pointer resources-trigger" [matMenuTriggerFor]="resourcesMenu">
<i class="al-icon al-ico-help-outlined" data-id="help Icon"></i>
</a>
<a class="pointer menu-trigger position-relative" [matMenuTriggerFor]="profileMenu">
<img alt="avatar" class="avatar" *ngIf="(user | async).avatar; else iconAvatar" [src]="(user | async).avatar">
<img alt="avatar" class="avatar" data-id="Avatar" *ngIf="(user | async).avatar; else iconAvatar" [src]="(user | async).avatar">
<ng-template #iconAvatar>
<div class="user-icon">
<i class="al-icon al-ico-account sm-md"></i>
@@ -44,7 +44,7 @@
Settings
</button>
<sm-header-user-menu-actions></sm-header-user-menu-actions>
<button mat-menu-item (click)="logout()">
<button mat-menu-item (click)="logout()" data-id="Logout">
<span class="al-ico-logout al-icon icon sm-md"></span>
Logout
</button>
@@ -71,7 +71,7 @@
<i class="al-icon sm-md al-ico-applications"></i>ClearML Apps Introduction
</button>
</ng-container>
<button mat-menu-item (click)="navigate('https://www.clear.ml/contact-us/')">
<button mat-menu-item (click)="navigate('mailto:support@clear.ml')">
<i class="al-icon sm-md al-ico-email"></i>Contact Us
</button>
</mat-menu>

View File

@@ -1,5 +1,5 @@
@import "../../layout/layout";
@import "../../shared/ui-components/styles/variables";
@import "variables";
:host {
display: block;
@@ -40,16 +40,18 @@
width: 95%;
}
.resources-trigger {
display: flex;
color: $blue-300;
&:hover {
text-decoration: none;
color: $blue-200;
}
}
sm-common-search, .resources-trigger {
sm-common-search,
.resources-trigger {
margin-right: 24px;
background-color: $header-backgroud;
i {
font-size: 24px;
line-height: 24px;
color: $light-grey-blue;
}
}
.menu-trigger {
@@ -75,7 +77,7 @@
flex: 0 0 32px;
width: 32px;
height: 32px;
background-color: $light-grey-blue;
background-color: $blue-300;
color: $header-backgroud;
border-radius: 50%;

View File

@@ -1,4 +1,4 @@
@import "../../shared/ui-components/styles/variables";
@import "variables";
$lg-breakpoint: map-get($grid-breakpoints, lg);
:host {
@@ -7,6 +7,7 @@ $lg-breakpoint: map-get($grid-breakpoints, lg);
margin-left: $side-bar-close-width;
.nav-bar-items-container {
display: flex;
gap: 1px;
position: absolute;
left: 50%;
transform: translateX(-50%);

View File

@@ -1,4 +1,4 @@
import {Component, OnDestroy, OnInit} from '@angular/core';
import {ChangeDetectionStrategy, Component, OnDestroy, OnInit} from '@angular/core';
import {select, Store} from '@ngrx/store';
import { MatDialog, MatDialogRef } from '@angular/material/dialog';
import {selectNotification} from '../../core/reducers/view.reducer';
@@ -10,7 +10,8 @@ import {ConfirmDialogComponent} from '../../shared/ui-components/overlay/confirm
@Component({
selector : 'sm-server-notification-dialog-container',
templateUrl: './server-notification-dialog-container.component.html',
styleUrls : ['./server-notification-dialog-container.component.scss']
styleUrls : ['./server-notification-dialog-container.component.scss'],
changeDetection: ChangeDetectionStrategy.OnPush
})
export class ServerNotificationDialogContainerComponent implements OnInit, OnDestroy {
private notificationSubscription: Subscription;

View File

@@ -1,5 +1,4 @@
import { Component, OnInit } from '@angular/core';
// import {mobilecheck} from '../shared/utils/mobile';
import {CommercialContext, Environment} from '../../../environments/base';
import {ConfigurationService} from '@common/shared/services/configuration.service';
@@ -14,11 +13,6 @@ export class LoginPageComponent implements OnInit {
public commercialContext: CommercialContext;
public environment: Environment;
constructor() {
// this.mobile = mobilecheck();
}
ngOnInit(): void {
this.environment = ConfigurationService.globalEnvironment;
this.commercialContext = ConfigurationService.globalEnvironment.communityContext;

View File

@@ -58,7 +58,6 @@
<a class="link" target="_blank"
href="https://join.slack.com/t/allegroai-trains/shared_invite/enQtOTQyMTI1MzQxMzE4LTY5NTUxOTY1NmQ1MzQ5MjRhMGRhZmM4ODE5NTNjMTg2NTBlZGQzZGVkMWU3ZDg1MGE1MjQxNDEzMWU2NmVjZmY">
<i class="al-icon al-ico-slack sm"></i><span class="text">Community on Slack</span></a>
<a class="link" *ngIf="true" href="https://www.clear.ml/contact-us" target="_blank"><i class="al-icon al-ico-email sm"></i><span class="text">Contact
Us</span></a>
<a class="link" href="mailto:support@clear.ml" target="_blank"><i class="al-icon al-ico-email sm"></i><span class="text">Contact us</span></a>
</div>
</div>

View File

@@ -42,7 +42,7 @@
[name]="'metadataItemValue-'|uuid"
matInput>
</mat-form-field>
<i (click)="removeRow(i)" class="al-icon al-ico-trash al-color blue-300 sm-md pointer flashing-icon mb-2"></i>
<i (click)="removeRow(i)" class="al-icon al-ico-trash al-color blue-400 sm-md pointer flashing-icon mb-2"></i>
</div>
<button class="btn btn-cml-primary plus mt-2" (click)="addRow()"><i
class="al-icon al-color blue-400 sm al-ico-plus d-flex align-items-center"></i>

View File

@@ -1,5 +1,5 @@
<sm-menu
[iconClass]="'al-icon al-ico-settings al-color pointer create-new-icon ' + (disabled ? 'blue-600' : 'blue-400')"
[iconClass]="'al-icon al-ico-settings al-color pointer create-new-icon ' + (disabled ? 'blue-600' : 'blue-300')"
buttonTooltip="Customize table"
smMenuClass="light-theme custom-columns"
[showButton]="false"

View File

@@ -3,7 +3,7 @@
.custom-column-buttons {
display: flex;
font-weight: 500;
color: $light-grey-blue;
color: $blue-400;
background: $faint-gray;
cursor: pointer;
.add-button {
@@ -21,7 +21,7 @@
}
div:not(.disabled):hover {
color: $blue-400;
color: $blue-300;
transition: color 0.3s;
}
}

View File

@@ -1,15 +1,15 @@
import {ChangeDetectionStrategy, Component, EventEmitter, Input, Output} from '@angular/core';
import {getOr} from 'lodash/fp';
import {SelectedModel} from '../../shared/models.model';
import {NA} from '../../../../app.constants';
import {TAGS} from '../../../tasks/tasks.constants';
import {NA} from '~/app.constants';
import {TAGS} from '@common/tasks/tasks.constants';
import {DatePipe} from '@angular/common';
import {TIME_FORMAT_STRING} from '../../../constants';
import {TIME_FORMAT_STRING} from '@common/constants';
import {Store} from '@ngrx/store';
import {ActivateModelEdit, CancelModelEdit} from '../../actions/models-info.actions';
import {AdminService} from '~/shared/services/admin.service';
import {getSignedUrl} from '../../../core/actions/common-auth.actions';
import {selectSignedUrl} from '../../../core/reducers/common-auth-reducer';
import {getSignedUrl} from '@common/core/actions/common-auth.actions';
import {selectSignedUrl} from '@common/core/reducers/common-auth-reducer';
import {filter, map, take} from 'rxjs/operators';
@Component({
@@ -22,7 +22,7 @@ export class ModelGeneralInfoComponent {
constructor(private datePipe: DatePipe, private store: Store<any>, private adminService: AdminService) {
}
public kpis: {label: string; value: string, downloadable?: boolean; href?: string; task?: string}[];
public kpis: {label: string; value: string; downloadable?: boolean; href?: string; task?: string}[];
private _model: SelectedModel;
public isLocalFile: boolean;

View File

@@ -42,7 +42,7 @@
</div>
<div class="input-container col-2 remove-step">
<i (click)="removeRow(i)" class="al-icon al-ico-trash al-color blue-300 sm-md pointer flashing-icon mb-2"></i>
<i (click)="removeRow(i)" class="al-icon al-ico-trash al-color blue-400 sm-md pointer flashing-icon mb-2"></i>
</div>
</div>
</div>

View File

@@ -12,11 +12,11 @@ import * as viewActions from '../actions/models-view.actions';
import {ModelInfoState} from '../reducers/model-info.reducer';
import {MODELS_INFO_ONLY_FIELDS} from '../shared/models.const';
import {selectSelectedModel} from '../reducers';
import {EmptyAction} from '~/app.constants';
import {SelectedModel} from '../shared/models.model';
import {selectActiveWorkspace} from '../../core/reducers/users-reducer';
import {isExample, isSharedAndNotOwner} from '../../shared/utils/shared-utils';
import {resetActiveSection} from '../actions/models-info.actions';
import {getTags} from '@common/core/actions/projects.actions';
@Injectable()
export class ModelsInfoEffects {
@@ -116,10 +116,11 @@ export class ModelsInfoEffects {
const changes = res?.fields || action.changes;
return [
viewActions.updateModel({id: action.id, changes}),
selectedModel?.id === action.id ? new infoActions.ModelDetailsUpdated({
id: action.id,
changes
}) : new EmptyAction()
...(selectedModel?.id === action.id ?
[new infoActions.ModelDetailsUpdated({ id: action.id, changes })]
: []
),
...(changes.tags ? [getTags()] : [])
];
}),
catchError(err => [

View File

@@ -50,6 +50,7 @@ import {PublishFooterItem} from '../shared/entity-page/footer-items/publish-foot
import {HasReadOnlyFooterItem} from '../shared/entity-page/footer-items/has-read-only-footer-item';
import {SelectedTagsFooterItem} from '../shared/entity-page/footer-items/selected-tags-footer-item';
import {RefreshService} from '@common/core/services/refresh.service';
import {SmSyncStateSelectorService} from '../core/services/sync-state-selector.service';
@Component({
@@ -103,9 +104,10 @@ export class ModelsComponent extends BaseEntityPageComponent implements OnInit,
protected route: ActivatedRoute,
protected router: Router,
protected dialog: MatDialog,
protected refresh: RefreshService
protected refresh: RefreshService,
protected syncSelector: SmSyncStateSelectorService,
) {
super(store, route, router, dialog, refresh);
super(store, route, router, dialog, refresh, syncSelector);
this.selectSplitSize$ = this.store.select(modelsSelectors.selectSplitSize);
this.tableSortFields$ = this.store.select(modelsSelectors.selectTableSortFields);
this.selectedModel$ = this.store.select(modelsSelectors.selectSelectedTableModel);

View File

@@ -167,7 +167,7 @@ export class ModelsTableComponent extends BaseTableView {
this.filtersValues[MODELS_TABLE_COL_FIELDS.PROJECT] = get([MODELS_TABLE_COL_FIELDS.PROJECT, 'value'], filters) || [];
this.filtersMatch[MODELS_TABLE_COL_FIELDS.TAGS] = filters?.[MODELS_TABLE_COL_FIELDS.TAGS]?.matchMode || '';
this.filtersSubValues[MODELS_TABLE_COL_FIELDS.TAGS] = get(['system_tags', 'value'], filters) || [];
//dynamic filters
// dynamic filters
const filtersValues = createFiltersFromStore(filters || {}, false);
this.filtersValues = Object.assign({}, {...this.filtersValues}, {...filtersValues});
}
@@ -315,10 +315,10 @@ export class ModelsTableComponent extends BaseTableView {
}
this.singleRowContext = !!data?.single;
this.menuBackdrop = !!data?.backdrop;
if(!data?.single) {
if (!data?.single) {
this.contextModel = this.models.find(model => model.id === data.rowData.id);
if (!this.selectedModels.map(model => model.id).includes(this.contextModel.id)) {
this.prevSelected = this.contextModel;
this.prevSelected = this.contextModel.id;
this.emitSelection([this.contextModel]);
}
} else {

View File

@@ -53,7 +53,7 @@ $log-header-height: 48px;
column-gap: 24px;
padding: 0 24px;
background-color: $blue-700;
border-bottom: 1px solid #303443;
border-bottom: 1px solid $dark-border;
color: $blue-100;
box-shadow: 0 -2px 6px rgb(0 0 0 / 40%);

View File

@@ -145,7 +145,6 @@ export class PipelineControllerInfoComponent implements OnInit, AfterViewInit, O
.pipe(filter(model => model?.length > 0), tap((model: any[]) =>
model.forEach(row => this.chartWidth = Math.max(this.chartWidth, row.length * 300))));
const pipelineObject = this.getTreeObject(task);
if (pipelineObject) {
this.pipelineController = this.convertPipelineToDagModel(pipelineObject);
this.resetUninitializedRunningFields();
this._dagManager.setNewItemsArrayAsDagModel(this.pipelineController);
@@ -158,7 +157,7 @@ export class PipelineControllerInfoComponent implements OnInit, AfterViewInit, O
this.drawLines();
this.cdr.detectChanges();
}, 0);
}
})
);
}

Some files were not shown because too many files have changed in this diff Show More