release v2.0 (#98)

Co-authored-by: shallegro <shay@allego.ai>
This commit is contained in:
shyallegro 2025-01-01 10:29:37 +02:00 committed by GitHub
parent ff27bad900
commit 946d6ec9ae
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
1440 changed files with 24872 additions and 24686 deletions

View File

@ -19,7 +19,7 @@ jobs:
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '18'
node-version: '22'
cache: 'npm'
- name: Install dependencies
@ -29,4 +29,4 @@ jobs:
run: npm run build
- name: Check for production build
run: test -f build/index.html
run: test -f build/browser/index.html

View File

@ -6,30 +6,35 @@
"trains-webapp": {
"root": "",
"sourceRoot": "src",
"prefix": "sm",
"projectType": "application",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser-esbuild",
"builder": "@angular-devkit/build-angular:application",
"options": {
"preserveSymlinks": true,
"outputPath": "build",
"outputPath": {
"base": "build"
},
"index": "src/index.html",
"main": "src/main.ts",
"tsConfig": "src/tsconfig.app.json",
"polyfills": [
"zone.js"
],
"tsConfig": "tsconfig.app.json",
"inlineStyleLanguage": "scss",
"stylePreprocessorOptions": {
"includePaths": [
"src/app/webapp-common/shared/ui-components/styles/"
"",
"src/app/webapp-common/shared/ui-components/styles/",
"src/app/webapp-common/styles/",
"."
]
},
"assets": [
"src/assets",
"src/favicon.ico",
"src/env.js",
"src/version.json",
"src/404.html",
"src/manifest.webmanifest",
"src/app/webapp-common/assets",
{
"glob": "**/*",
@ -38,11 +43,12 @@
}
],
"styles": [
"node_modules/primeng/resources/components/table/table.css",
"node_modules/primeicons/primeicons.css",
"node_modules/ngx-markdown-editor/assets/highlight.js/agate.min.css",
"src/styles.scss",
"src/fonts.scss"
{
"bundleName": "global-styles",
"inject": true,
"input": "src/app/webapp-common/styles/style.scss"
}
],
"scripts": [
"node_modules/ngx-markdown-editor/assets/highlight.js/highlight.min.js"
@ -56,27 +62,29 @@
"fast-xml-parser",
"url",
"@aws-crypto/sha256-browser",
"@aws-crypto/crc32",
"@aws-crypto/sha1-browser",
"@aws-crypto/crc32",
"@aws-crypto/crc32c",
"bowser",
"@smithy/util-defaults-mode-browser",
"filesize/lib/filesize.es6",
"hex-rgb",
"ace-builds",
"localforage",
"dom-to-image",
"ace-builds",
"hocon-parser",
"taira",
"base64",
"base-64",
"export-to-csv",
"dompurify",
"hammerjs"
],
"extractLicenses": false,
"buildOptimizer": false,
"sourceMap": true,
"optimization": false,
"namedChunks": true
"namedChunks": true,
"browser": "src/main.ts"
},
"configurations": {
"appdev": {
@ -91,7 +99,6 @@
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
@ -111,7 +118,6 @@
"sourceMap": false,
"namedChunks": false,
"extractLicenses": true,
"buildOptimizer": true,
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
@ -121,7 +127,8 @@
"replace": "src/app/build-specifics/index.ts",
"with": "src/app/build-specifics/index.prod.ts"
}
]
],
"serviceWorker": false
}
},
"defaultConfiguration": ""
@ -158,22 +165,23 @@
"main": "src/test.ts",
"karmaConfig": "./karma.conf.js",
"polyfills": [
"zone.js",
"zone.js/testing"
"zone.js"
],
"stylePreprocessorOptions": {
"includePaths": [
"src/app/webapp-common/shared/ui-components/styles/"
]
},
"tsConfig": "src/tsconfig.spec.json",
"tsConfig": "./tsconfig.spec.json",
"styles": [
"src/styles.scss"
],
"stylePreprocessorOptions": {
"includePaths": [
"",
"src/app/webapp-common/shared/ui-components/styles/"
]
},
"assets": [
"src/assets",
"src/favicon.ico",
"src/version.json",
"src/manifest.webmanifest",
"src/app/webapp-common/assets"
]
}
@ -201,30 +209,34 @@
"prefix": "app",
"architect": {
"build": {
"builder": "@angular-devkit/build-angular:browser-esbuild",
"builder": "@angular-devkit/build-angular:application",
"options": {
"preserveSymlinks": true,
"outputPath": "dist/report-widgets",
"baseHref": "widgets",
"index": "src/app/webapp-common/clearml-applications/report-widgets/src/index.html",
"main": "src/app/webapp-common/clearml-applications/report-widgets/src/main.ts",
"outputPath": {
"base": "dist/report-widgets"
},
"polyfills": [
"zone.js"
],
"baseHref": "",
"index": "src/app/webapp-common/clearml-applications/report-widgets/src/index.html",
"tsConfig": "src/app/webapp-common/clearml-applications/report-widgets/tsconfig.app.json",
"inlineStyleLanguage": "scss",
"assets": [
"src/app/webapp-common/clearml-applications/report-widgets/src/favicon.ico",
"src/app/webapp-common/clearml-applications/report-widgets/src/assets",
"src/app/webapp-common/clearml-applications/report-widgets/src/app/webapp-common/assets"
"src/app/webapp-common/clearml-applications/report-widgets/src/app/webapp-common/assets",
"src/app/webapp-common/clearml-applications/report-widgets/src/app/webapp-common/assets/fonts/trains.ttf"
],
"stylePreprocessorOptions": {
"includePaths": [
"",
"src/app/webapp-common/styles/",
"src/app/webapp-common/shared/ui-components/styles/"
]
},
"styles": [
"src/app/webapp-common/clearml-applications/report-widgets/src/styles.scss",
"src/app/webapp-common/clearml-applications/report-widgets/src/app/styles/styles.scss",
{
"input": "src/app/webapp-common/assets/fonts/trains-icons.scss",
"bundleName": "trains-icons",
@ -233,18 +245,20 @@
],
"scripts": [],
"allowedCommonJsDependencies": [
"string-to-color",
"dom-to-image",
"dompurify",
"url",
"taira",
"@aws-crypto/crc32",
"@aws-crypto/crc32c",
"fast-xml-parser",
"@aws-crypto/sha1-browser",
"@aws-crypto/sha256-browser",
"fast-xml-parser",
"bowser"
]
"@aws-crypto/crc32",
"@aws-crypto/crc32c",
"bowser",
"hammerjs",
"dom-to-image",
"dompurify",
"string-to-color",
"taira",
"url"
],
"browser": "src/app/webapp-common/clearml-applications/report-widgets/src/main.ts"
},
"configurations": {
"production": {
@ -257,7 +271,7 @@
{
"type": "anyComponentStyle",
"maximumWarning": "2kb",
"maximumError": "4kb"
"maximumError": "5kb"
}
],
"fileReplacements": [
@ -269,7 +283,6 @@
"outputHashing": "all"
},
"development": {
"buildOptimizer": false,
"optimization": false,
"extractLicenses": false,
"sourceMap": true,
@ -313,16 +326,10 @@
"assets": [
"src/app/webapp-common/clearml-applications/report-widgets/src/favicon.ico",
"src/app/webapp-common/clearml-applications/report-widgets/src/assets",
"src/app/webapp-common/clearml-applications/report-widgets/src/app/webapp-common/assets",
"src/app/webapp-common/clearml-applications/report-widgets/src/app/webapp-common/assets/fonts/trains.ttf"
"src/app/webapp-common/clearml-applications/report-widgets/src/app/webapp-common/assets"
],
"styles": [
"src/app/webapp-common/clearml-applications/report-widgets/src/styles.scss",
{
"input": "src/app/webapp-common/assets/fonts/trains-icons.scss",
"bundleName": "trains-icons",
"inject": false
}
"src/app/webapp-common/clearml-applications/report-widgets/src/styles.scss"
],
"scripts": []
}
@ -335,14 +342,20 @@
"prefix": "sm",
"style": "scss"
},
"@angular-eslint/schematics:application": {
"setParserOptionsProject": true
"al-schematics:component": {
"prefix": "sm",
"styleext": "scss"
},
"@angular-eslint/schematics:library": {
"setParserOptionsProject": true
"al-schematics:directive": {
"prefix": "sm"
}
},
"cli": {
"analytics": false
"analytics": false,
"schematicCollections": [
"@angular-eslint/schematics",
"@ngrx/schematics",
"ngxtension"
]
}
}

3150
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.17.0",
"version": "2.0.0",
"license": "",
"scripts": {
"ng": "ng",
@ -19,21 +19,21 @@
},
"private": true,
"dependencies": {
"@angular/animations": "^18.2.10",
"@angular/cdk": "^18.2.11",
"@angular/common": "^18.2.10",
"@angular/compiler": "^18.2.10",
"@angular/core": "^18.2.10",
"@angular/forms": "^18.2.10",
"@angular/material": "^18.2.11",
"@angular/platform-browser": "^18.2.10",
"@angular/platform-browser-dynamic": "^18.2.10",
"@angular/platform-server": "^18.2.10",
"@angular/router": "^18.2.10",
"@angular/service-worker": "^18.2.10",
"@angular/youtube-player": "^18.2.11",
"@aws-sdk/client-s3": "^3.637.0",
"@aws-sdk/s3-request-presigner": "^3.637.0",
"@angular/animations": "^18.2.12",
"@angular/cdk": "^18.2.14",
"@angular/common": "^18.2.12",
"@angular/compiler": "^18.2.12",
"@angular/core": "^18.2.12",
"@angular/forms": "^18.2.12",
"@angular/material": "^18.2.14",
"@angular/platform-browser": "^18.2.12",
"@angular/platform-browser-dynamic": "^18.2.12",
"@angular/platform-server": "^18.2.12",
"@angular/router": "^18.2.12",
"@angular/service-worker": "^18.2.12",
"@angular/youtube-player": "^18.2.14",
"@aws-sdk/client-s3": "^3.693.0",
"@aws-sdk/s3-request-presigner": "^3.693.0",
"@ctrl/ngx-github-buttons": "^9.0.0",
"@ctrl/tinycolor": "^4.1.0",
"@ngneat/dag": "^2.0.0",
@ -43,23 +43,24 @@
"@ngrx/operators": "^18.1.1",
"@ngrx/router-store": "^18.1.1",
"@ngrx/store": "^18.1.1",
"ace-builds": "^1.36.2",
"@typescript-eslint/types": "^8.15.0",
"ace-builds": "^1.36.5",
"angular-resizable-element": "^7.0.2",
"angular-split": "^17.2.0",
"angular-split": "^18.0.0",
"ansi-to-html": "^0.7.2",
"bootstrap": "^5.3.3",
"chart.js": "^4.4.4",
"chart.js": "^4.4.6",
"chartjs-adapter-date-fns": "^3.0.0",
"chartjs-plugin-annotation": "^3.0.1",
"chartjs-plugin-zoom": "^2.0.1",
"chartjs-plugin-annotation": "^3.1.0",
"chartjs-plugin-zoom": "^2.1.0",
"curved-arrows": "^0.3.0",
"d3-selection": "^3.0.0",
"date-fns": "^3.6.0",
"diff": "^5.2.0",
"date-fns": "^4.1.0",
"diff": "^7.0.0",
"dom-to-image": "^2.6.0",
"dompurify": "^3.1.6",
"export-to-csv": "^1.3.0",
"filesize": "^10.1.4",
"dompurify": "^3.2.0",
"export-to-csv": "^1.4.0",
"filesize": "^10.1.6",
"has-ansi": "^6.0.0",
"hocon-parser": "^1.0.1",
"lodash-es": "^4.17.21",
@ -75,43 +76,44 @@
"ngxtension": "^4.1.0",
"object-hash": "^3.0.0",
"primeicons": "^7.0.0",
"primeng": "^17.18.9",
"primeng": "^17.18.15",
"rxjs": "^7.8.1",
"string-to-color": "^2.2.2",
"taira": "^3.2.2",
"tslib": "^2.7.0",
"tslib": "^2.8.1",
"url": "^0.11.4",
"uuid": "^10.0.0",
"uuid": "^11.0.3",
"zone.js": "~0.15.0"
},
"devDependencies": {
"@angular-devkit/build-angular": "^18.2.11",
"@angular-devkit/core": "^18.2.11",
"@angular-devkit/schematics": "^18.2.11",
"@angular-devkit/schematics-cli": "^18.2.1",
"@angular/cli": "^18.2.11",
"@angular/compiler-cli": "^18.2.10",
"@angular/language-service": "^18.2.10",
"@fortawesome/fontawesome-free": "^6.6.0",
"@angular-devkit/build-angular": "^18.2.12",
"@angular-devkit/core": "^18.2.12",
"@angular-devkit/schematics": "^18.2.12",
"@angular-devkit/schematics-cli": "^18.2.12",
"@angular/cli": "^18.2.12",
"@angular/compiler-cli": "^18.2.12",
"@angular/language-service": "^18.2.12",
"@ngrx/eslint-plugin": "^18.1.1",
"@ngrx/schematics": "^18.1.1",
"@ngrx/store-devtools": "^18.1.1",
"@types/d3-selection": "^3.0.10",
"@types/d3-selection": "^3.0.11",
"@types/diff": "^6.0.0",
"@types/dom-to-image": "^2.6.7",
"@types/dompurify": "^3.0.5",
"@types/has-ansi": "^5.0.2",
"@types/jasmine": "^5.1.4",
"@types/lodash-es": "^4.17.12",
"@types/node": "^20.14.10",
"@types/plotly.js": "^2.33.3",
"@types/node": "^22.9.0",
"@types/plotly.js": "^2.35.0",
"@types/tinycolor2": "^1.4.6",
"@types/uuid": "^10.0.0",
"angular-eslint": "^18.3.0",
"eslint": "^9.9.1",
"eslint-plugin-import": "2.29.1",
"eslint-plugin-jsdoc": "^50.2.2",
"angular-eslint": "^18.4.1",
"eslint": "^9.15.0",
"eslint-plugin-import": "2.31.0",
"eslint-plugin-jsdoc": "^50.5.0",
"eslint-plugin-prefer-arrow": "1.2.3",
"primeng-sass-theme": "github:primefaces/primeng-sass-theme",
"typescript": "^5.5.4",
"typescript-eslint": "^8.2.0"
"typescript-eslint": "^8.15.0"
}
}

21
src/app/app.component.html Executable file → Normal file
View File

@ -1,17 +1,20 @@
<sm-update-notifier #update
[availableUpdates]="!hideUpdate && (updatesAvailable$ | async)"
[currentUser]="currentUser"
[dismissedVersion]="serverUpdatesService.lastDismissedVersion"
(versionDismissed)="versionDismissed($event)"
(notifierActive)="notifierActive($event)">
</sm-update-notifier>
[availableUpdates]="(updatesAvailable$ | async)"
[currentUser]="$any(currentUser())"
[dismissedVersion]="serverUpdatesService.lastDismissedVersion"
(versionDismissed)="versionDismissed($event)"
></sm-update-notifier>
<sm-color-picker-wrapper id="color-picker-outlet"></sm-color-picker-wrapper>
<sm-server-notification-dialog-container></sm-server-notification-dialog-container>
<sm-spinner></sm-spinner>
<div class="root-container">
<sm-side-nav *ngIf="currentUser"></sm-side-nav>
<div class="app-container" [class.login-page]="!currentUser" [class.notifier-open]="update?.active">
<sm-header *ngIf="currentUser" [isLogin]="isLoginContext" [isShareMode]="isSharedAndNotOwner$ | async"></sm-header>
@if (currentUser()) {
<sm-side-nav></sm-side-nav>
}
<div class="app-container" [class.login-page]="!currentUser()" [class.notifier-open]="update?.active()">
@if (currentUser()) {
<sm-header [isLogin]="loginContext | ngrxPush" [isShareMode]="isSharedAndNotOwner()"></sm-header>
}
<router-outlet class="main-router"></router-outlet>
</div>
</div>

6
src/app/app.component.scss Executable file → Normal file
View File

@ -1,4 +1,4 @@
@import "webapp-common/shared/ui-components/styles/variables";
@import "variables";
@import "webapp-common/layout/layout.scss";
$notifier-height: 30px;
@ -18,6 +18,7 @@ notification-container {
flex: 1;
height: 100%;
width: calc(100% - #{$side-bar-close-width});
background-color: var(--color-surface);
&.notifier-open {
height: calc(100% - #{$notifier-height});
@ -52,4 +53,5 @@ iframe.iframe-maximized {
height: 100%;
width: 100%;
z-index: 999;
}
background-color: var(--color-surface);
}

114
src/app/app.component.ts Executable file → Normal file
View File

@ -1,18 +1,14 @@
import {selectCurrentUser} from '@common/core/reducers/users-reducer';
import {Component, OnDestroy, OnInit, ViewEncapsulation, HostListener, Renderer2, inject} from '@angular/core';
import {Component, ViewEncapsulation, HostListener, Renderer2, inject, ChangeDetectionStrategy} from '@angular/core';
import {NavigationEnd, Router} from '@angular/router';
import {Title} from '@angular/platform-browser';
import {selectBreadcrumbs, selectLoggedOut} from '@common/core/reducers/view.reducer';
import {Store} from '@ngrx/store';
import {selectRouterParams, selectRouterUrl} from '@common/core/reducers/router-reducer';
import {Project} from './business-logic/model/projects/project';
import {getAllSystemProjects, setSelectedProjectId, updateProject} from '@common/core/actions/projects.actions';
import {selectRouterProjectId, selectSelectedProject} from '@common/core/reducers/projects.reducer';
import {selectRouterUrl} from '@common/core/reducers/router-reducer';
import {getAllSystemProjects, setSelectedProjectId} from '@common/core/actions/projects.actions';
import {selectRouterProjectId} from '@common/core/reducers/projects.reducer';
import {getTutorialBucketCredentials} from '@common/core/actions/common-auth.actions';
import {termsOfUseAccepted} from '@common/core/actions/users.actions';
import {distinctUntilChanged, filter, tap} from 'rxjs/operators';
import {distinctUntilChanged, filter, map} from 'rxjs/operators';
import * as routerActions from './webapp-common/core/actions/router.actions';
import {combineLatest, Observable, Subscription} from 'rxjs';
import {ServerUpdatesService} from '@common/shared/services/server-updates.service';
import {selectAvailableUpdates} from './core/reducers/view.reducer';
import {UPDATE_SERVER_PATH} from './app.constants';
@ -23,63 +19,42 @@ import {ConfigurationService} from '@common/shared/services/configuration.servic
import {selectIsSharedAndNotOwner} from './features/experiments/reducers';
import {TipsService} from '@common/shared/services/tips.service';
import {USER_PREFERENCES_KEY} from '@common/user-preferences';
import {Environment} from '../environments/base';
import {loadExternalLibrary} from '@common/shared/utils/load-external-library';
import {User} from '~/business-logic/model/users/user';
import {BreadcrumbsService} from '@common/shared/services/breadcrumbs.service';
import {ThemeService} from '@common/shared/services/theme.service';
import {takeUntilDestroyed} from '@angular/core/rxjs-interop';
@Component({
selector: 'sm-root',
templateUrl: 'app.component.html',
styleUrls: ['app.component.scss'],
encapsulation: ViewEncapsulation.None
changeDetection: ChangeDetectionStrategy.OnPush,
encapsulation: ViewEncapsulation.None,
providers: [ThemeService]
})
export class AppComponent implements OnInit, OnDestroy {
export class AppComponent {
private router = inject(Router);
private titleService = inject(Title);
private store = inject(Store);
public serverUpdatesService = inject(ServerUpdatesService);
private uiUpdatesService = inject(UiUpdatesService);
private tipsService = inject(TipsService);
private renderer = inject(Renderer2);
private configService = inject(ConfigurationService);
private config = inject(ConfigurationService);
private breadcrumbsService = inject(BreadcrumbsService); // don't delete
protected loggedOut$: Observable<boolean>;
private urlSubscription: Subscription;
protected selectedProject$: Observable<Project>;
protected projectId: string;
protected isWorkersContext: boolean;
protected updatesAvailable$: Observable<string>;
private breadcrumbsSubscription: Subscription;
private selectedCurrentUserSubscription: Subscription;
protected showNotification = true;
protected isLoginContext: boolean;
protected currentUser: User;
protected isSharedAndNotOwner$: Observable<boolean>;
private activeWorkspace: string;
protected hideUpdate: boolean;
protected showSurvey: boolean;
private environment: Environment;
private title = 'ClearML';
private themeService = inject(ThemeService); // don't delete
protected updatesAvailable$ = this.store.select(selectAvailableUpdates);
protected currentUser = this.store.selectSignal(selectCurrentUser);
protected isSharedAndNotOwner = this.store.selectSignal(selectIsSharedAndNotOwner);
protected loginContext = this.store.select(selectRouterUrl).pipe(map(url => url?.includes('login')));
@HostListener('document:visibilitychange') onVisibilityChange() {
this.store.dispatch(visibilityChanged({visible: !document.hidden}));
}
@HostListener('window:beforeunload', ['$event'])
beforeunloadHandler() {
window.localStorage.setItem('lastWorkspace', this.activeWorkspace);
}
constructor() {
constructor(
) {
this.loggedOut$ = this.store.select(selectLoggedOut);
this.isSharedAndNotOwner$ = this.store.select(selectIsSharedAndNotOwner);
this.selectedProject$ = this.store.select(selectSelectedProject);
this.updatesAvailable$ = this.store.select(selectAvailableUpdates);
}
ngOnInit(): void {
window.addEventListener('message', e => {
if (e.data.maximizing) {
const drawerContent = document.querySelector('sm-report mat-drawer-container');
@ -94,20 +69,16 @@ export class AppComponent implements OnInit, OnDestroy {
}
});
this.configService.globalEnvironmentObservable.subscribe(env => {
this.hideUpdate = env.hideUpdateNotice;
this.showSurvey = env.showSurvey;
this.environment = env;
});
this.router.events
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe(() => this.store.dispatch(routerActions.navigationEnd()));
this.selectedCurrentUserSubscription = this.store.select(selectCurrentUser).pipe(
tap(user => this.currentUser = user as unknown as User),
filter(user => !!user?.id),
distinctUntilChanged((prev, next) => prev?.id === next?.id)
)
this.store.select(selectCurrentUser)
.pipe(
takeUntilDestroyed(),
filter(user => !!user?.id),
distinctUntilChanged((prev, next) => prev?.id === next?.id)
)
.subscribe(() => {
this.store.dispatch(getAllSystemProjects());
this.store.dispatch(getTutorialBucketCredentials());
@ -127,28 +98,11 @@ export class AppComponent implements OnInit, OnDestroy {
this.store.dispatch(setSelectedProjectId({projectId}));
});
this.urlSubscription = combineLatest([
this.store.select(selectRouterUrl),
this.store.select(selectRouterParams)
])
.subscribe(([url, params]) => {
this.projectId = params?.projectId;
this.isLoginContext = url && url.includes('login');
this.isWorkersContext = url && url.includes('workers-and-queues');
});
this.breadcrumbsSubscription = this.store.select(selectBreadcrumbs).subscribe(breadcrumbs => {
const crumbs = breadcrumbs.flat().filter((breadcrumb => !!breadcrumb?.name)).map(breadcrumb => breadcrumb.name);
if (crumbs.length> 0) {
this.titleService.setTitle(`${this.title ? this.title + '-' : ''} ${crumbs.join(' / ')}`);
}
});
if (window.localStorage.getItem('disableHidpi') !== 'true') {
this.setScale();
}
loadExternalLibrary(this.store, this.environment.plotlyURL, plotlyReady);
loadExternalLibrary(this.store, this.config.configuration().plotlyURL, plotlyReady);
loadExternalLibrary(this.store, 'assets/ace-builds/ace.js', aceReady);
}
@ -162,21 +116,7 @@ export class AppComponent implements OnInit, OnDestroy {
this.renderer.setStyle(document.body, 'width', `${dimensionRatio}vw`);
}
nameChanged(name) {
this.store.dispatch(updateProject({id: this.projectId, changes: {name}}));
}
ngOnDestroy(): void {
this.urlSubscription.unsubscribe();
this.breadcrumbsSubscription.unsubscribe();
this.selectedCurrentUserSubscription.unsubscribe();
}
versionDismissed(version: string) {
this.serverUpdatesService.setDismissedVersion(version);
}
notifierActive(show: boolean) {
this.showNotification = show;
}
}

0
src/app/app.constants.ts Executable file → Normal file
View File

13
src/app/app.module.ts Executable file → Normal file
View File

@ -1,4 +1,4 @@
import {APP_INITIALIZER, NgModule} from '@angular/core';
import {APP_INITIALIZER, inject, NgModule} from '@angular/core';
import {BrowserModule} from '@angular/platform-browser';
import {BrowserAnimationsModule} from '@angular/platform-browser/animations';
import {PreloadAllModules, RouteReuseStrategy, RouterModule} from '@angular/router';
@ -27,7 +27,9 @@ import {MAT_TOOLTIP_DEFAULT_OPTIONS, MatTooltipDefaultOptions} from '@angular/ma
import {UpdateNotifierComponent} from '@common/shared/ui-components/overlay/update-notifier/update-notifier.component';
import {ChooseColorModule} from '@common/shared/ui-components/directives/choose-color/choose-color.module';
import {SpinnerComponent} from '@common/shared/ui-components/overlay/spinner/spinner.component';
import {MatIconRegistry} from '@angular/material/icon';
import {provideCharts, withDefaultRegisterables} from 'ng2-charts';
import {PushPipe} from '@ngrx/component';
@NgModule({
declarations : [AppComponent],
@ -62,6 +64,7 @@ import {provideCharts, withDefaultRegisterables} from 'ng2-charts';
UpdateNotifierComponent,
ChooseColorModule,
SpinnerComponent,
PushPipe,
],
providers: [
UserPreferences,
@ -84,4 +87,10 @@ import {provideCharts, withDefaultRegisterables} from 'ng2-charts';
bootstrap : [AppComponent],
exports : []
})
export class AppModule {}
export class AppModule {
public matIconRegistry = inject(MatIconRegistry);
constructor() {
this.matIconRegistry.registerFontClassAlias('al', 'al-icon');
}
}

41
src/app/app.routes.ts Executable file → Normal file
View File

@ -13,8 +13,8 @@ export const routes: Routes = [
},
{
path: 'projects',
loadChildren: () => import('./features/projects/projects.module').then(m => m.ProjectsModule),
data: {search: true},
loadChildren: () => import('./features/projects/projects.module').then(m => m.ProjectsModule),
},
{path: 'login', loadChildren: () => import('./features/login/login.module').then(m => m.LoginModule)},
{
@ -22,7 +22,6 @@ export const routes: Routes = [
loadChildren: () => import('./features/settings/settings.module').then(m => m.SettingsModule),
data: {search: false, workspaceNeutral: false, },
},
{
path: 'projects',
data: {search: true},
@ -33,15 +32,15 @@ export const routes: Routes = [
data: {search: true},
children: [
{path: '', pathMatch: 'full', children: [], canActivate: [projectRedirectGuardGuard]},
{path: '', redirectTo: '*', pathMatch: 'full'},
{
path: 'overview',
loadChildren: () => import('./webapp-common/project-info/project-info.module').then(m => m.ProjectInfoModule),
canDeactivate: [resetContextMenuGuard]
},
{path: 'projects', loadChildren: () => import('./features/projects/projects.module').then(m => m.ProjectsModule)},
{path: 'experiments', redirectTo: 'tasks'},
{
path: 'experiments',
path: 'tasks',
loadChildren: () => import('./features/experiments/experiments.module').then(m => m.ExperimentsModule),
canDeactivate: [resetContextMenuGuard]
},
@ -50,10 +49,12 @@ export const routes: Routes = [
loadChildren: () => import('./webapp-common/models/models.module').then(m => m.ModelsModule),
canDeactivate: [resetContextMenuGuard]
},
{path: 'compare-experiments', redirectTo: 'compare-tasks'},
{
path: 'compare-experiments',
loadChildren: () => import('./webapp-common/experiments-compare/experiments-compare.module').then(m => m.ExperimentsCompareModule),
path: 'compare-tasks',
data: {entityType: EntityTypeEnum.experiment},
loadChildren: () =>
import('./webapp-common/experiments-compare/experiments-compare.module').then(m => m.ExperimentsCompareModule)
},
{
path: 'compare-models',
@ -76,16 +77,27 @@ export const routes: Routes = [
{
path: ':projectId',
children: [
{path: 'pipelines', loadChildren: () => import('@common/pipelines/pipelines.module').then(m => m.PipelinesModule)},
{path: 'projects', loadComponent: () => import('@common/pipelines/nested-pipeline-page/nested-pipeline-page.component')
.then(m => m.NestedPipelinePageComponent)},
{
path: 'experiments', loadChildren: () => import('@common/pipelines-controller/pipelines-controller.module').then(m => m.PipelinesControllerModule)
path: 'pipelines',
loadChildren: () =>
import('@common/pipelines/pipelines.module').then(m => m.PipelinesModule)},
{
path: 'projects',
loadComponent: () =>
import('@common/pipelines/nested-pipeline-page/nested-pipeline-page.component').then(m => m.NestedPipelinePageComponent)
},
{path: 'experiments', redirectTo: 'tasks'},
{
path: 'compare-experiments',
path: 'tasks',
loadChildren: () =>
import('@common/pipelines-controller/pipelines-controller.module').then(m => m.PipelinesControllerModule)
},
{path: 'compare-experiments', redirectTo: 'compare-tasks'},
{
path: 'compare-tasks',
data: {entityType: EntityTypeEnum.controller},
loadChildren: () => import('./webapp-common/experiments-compare/experiments-compare.module').then(m => m.ExperimentsCompareModule)
loadChildren: () =>
import('./webapp-common/experiments-compare/experiments-compare.module').then(m => m.ExperimentsCompareModule)
},
]
},
@ -106,6 +118,11 @@ export const routes: Routes = [
loadChildren: () => import('./webapp-common/serving/serving.module').then(m => m.ServingModule),
canDeactivate: [resetContextMenuGuard]
},
{
path: 'enterprise',
loadChildren: () => import('@common/enterprise-visibility/enterprise.routes').then(r => r.routes),
canDeactivate: [resetContextMenuGuard],
},
{path: '404', loadChildren: () => import('./features/not-found/not-found.module').then(m => m.NotFoundModule)},
{path: '**', loadChildren: () => import('./features/not-found/not-found.module').then(m => m.NotFoundModule)},

View File

@ -2,6 +2,9 @@ import { StoreDevtoolsModule } from '@ngrx/store-devtools';
export const extCoreModules = [
StoreDevtoolsModule.instrument({
maxAge: 50
, connectInZone: true})
maxAge: 100,
trace: true,
traceLimit: 50,
connectInZone: true
})
];

View File

View File

@ -0,0 +1,103 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/* eslint-disable @typescript-eslint/no-unused-vars, @typescript-eslint/member-ordering */
import {HTTP} from '~/app.constants';
import {SmApiRequestsService} from './api-requests.service';
import { Inject, Injectable, Optional } from '@angular/core';
import { HttpClient, HttpHeaders, HttpParams,
HttpResponse, HttpEvent } from '@angular/common/http';
import { CustomHttpUrlEncodingCodec } from '../encoder';
import { ApiOptions } from './api';
import { Observable } from 'rxjs';
import { StorageGetSettingsResponse } from '../model/storage/storageGetSettingsResponse';
import { StorageResetSettingsRequest } from '../model/storage/storageResetSettingsRequest';
import { StorageResetSettingsResponse } from '../model/storage/storageResetSettingsResponse';
import { StorageSetSettingsRequest } from '../model/storage/storageSetSettingsRequest';
import { StorageSetSettingsResponse } from '../model/storage/storageSetSettingsResponse';
import { BASE_PATH, COLLECTION_FORMATS } from '../variables';
import { Configuration } from '../configuration';
@Injectable()
export class ApiStorageService {
protected basePath = HTTP.API_BASE_URL;
public defaultHeaders = new HttpHeaders({'Accept': 'application/json'});
public configuration = new Configuration();
constructor(protected apiRequest: SmApiRequestsService, @Optional()@Inject(BASE_PATH) basePath: string, @Optional() configuration: Configuration) {
if (basePath) {
this.basePath = basePath;
}
if (configuration) {
this.configuration = configuration;
this.basePath = basePath || configuration.basePath || this.basePath;
}
}
/**
* Internal. Get storage settings
* @param request request body
* @param options flags and headers to use in webapp
*/
public storageGetSettings(request: object, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<StorageGetSettingsResponse>(`${this.basePath}/storage.get_settings`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
/**
* Internal. Reset selected storage settings
* @param request request body
* @param options flags and headers to use in webapp
*/
public storageResetSettings(request: StorageResetSettingsRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<StorageResetSettingsResponse>(`${this.basePath}/storage.reset_settings`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
/**
* Internal. Set Storage settings
* @param request request body
* @param options flags and headers to use in webapp
*/
public storageSetSettings(request: StorageSetSettingsRequest, options?: ApiOptions): Observable<any> {
return this.apiRequest.post<StorageSetSettingsResponse>(`${this.basePath}/storage.set_settings`,
request,
{
headers: this.defaultHeaders,
withCredentials: this.configuration.withCredentials
},
options
);
}
}

4
src/app/business-logic/business-logic.module.ts Executable file → Normal file
View File

@ -18,6 +18,7 @@ import {ApiLoginService} from './api-services/login.service';
import {ApiPipelinesService} from '~/business-logic/api-services/pipelines.service';
import {ApiReportsService} from '~/business-logic/api-services/reports.service';
import {ApiServingService} from '~/business-logic/api-services/serving.service';
import {ApiStorageService} from '~/business-logic/api-services/storage.service';
@NgModule({
imports : [CommonModule, HttpClientModule],
@ -40,7 +41,8 @@ import {ApiServingService} from '~/business-logic/api-services/serving.service';
ApiLoginService,
ApiPipelinesService,
ApiReportsService,
ApiServingService
ApiServingService,
ApiStorageService
]
})
export class BusinessLogicModule {

0
src/app/business-logic/configuration.ts Executable file → Normal file
View File

0
src/app/business-logic/constants.ts Executable file → Normal file
View File

0
src/app/business-logic/encoder.ts Executable file → Normal file
View File

0
src/app/business-logic/model/al-task.ts Executable file → Normal file
View File

0
src/app/business-logic/model/api-request.ts Executable file → Normal file
View File

View File

@ -52,4 +52,5 @@ export interface Queue {
* Queue metadata
*/
metadata?: Array<MetadataItem>;
display_name: string;
}

View File

@ -25,4 +25,5 @@ export interface QueuesCreateRequest {
* System tags list. This field is reserved for system use, please don\'t use it.
*/
system_tags?: Array<string>;
display_name?: string
}

View File

@ -29,4 +29,5 @@ export interface QueuesUpdateRequest {
* System tags list. This field is reserved for system use, please don\'t use it.
*/
system_tags?: Array<string>;
display_name: string;
}

View File

@ -0,0 +1,44 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { AwsBucket } from '././awsBucket';
/**
* AWS S3 storage settings
*/
export interface Aws {
/**
* Access key
*/
key?: string;
/**
* Secret key
*/
secret?: string;
/**
* AWS region
*/
region?: string;
/**
* Access token
*/
token?: string;
/**
* If set then use host credentials
*/
use_credentials_chain?: boolean;
/**
* Credential settings per bucket
*/
buckets?: Array<AwsBucket>;
}

View File

@ -0,0 +1,67 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/**
* Settings per S3 bucket
*/
export interface AwsBucket {
/**
* The name of the bucket
*/
bucket?: string;
/**
* The path to match
*/
subdir?: string;
/**
* Host address (for minio servers)
*/
host?: string;
/**
* Access key
*/
key?: string;
/**
* Secret key
*/
secret?: string;
/**
* Access token
*/
token?: string;
/**
* Multipart upload
*/
multipart?: boolean;
/**
* ACL
*/
acl?: string;
/**
* Use SSL connection
*/
secure?: boolean;
/**
* AWS Region
*/
region?: string;
/**
* Verify server certificate
*/
verify?: boolean;
/**
* Use host configured credentials
*/
use_credentials_chain?: boolean;
}

View File

@ -0,0 +1,24 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { AzureContainer } from '././azureContainer';
/**
* Azure storage settings
*/
export interface Azure {
/**
* Credentials per container
*/
containers?: Array<AzureContainer>;
}

View File

@ -0,0 +1,31 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/**
* Azure container settings
*/
export interface AzureContainer {
/**
* Account name
*/
account_name?: string;
/**
* Account key
*/
account_key?: string;
/**
* The name of the container
*/
container_name?: string;
}

View File

@ -0,0 +1,24 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.12
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface Credentials {
/**
* Credentials access key
*/
access_key?: string;
/**
* Credentials secret key
*/
secret_key?: string;
}

View File

@ -0,0 +1,32 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { GoogleBucket } from '././googleBucket';
/**
* Google storage settings
*/
export interface Google {
/**
* Project name
*/
project?: string;
/**
* The contents of the credentials json file
*/
credentials_json?: string;
/**
* Credentials per bucket
*/
buckets?: Array<GoogleBucket>;
}

View File

@ -0,0 +1,35 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.30
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
/**
* Settings per Google storage bucket
*/
export interface GoogleBucket {
/**
* The name of the bucket
*/
bucket?: string;
/**
* The name of the project
*/
project?: string;
/**
* The path to match
*/
subdir?: string;
/**
* The contents of the credentials json file
*/
credentials_json?: string;
}

View File

@ -0,0 +1,8 @@
export * from './credentials';
export * from './storage';
export * from './storageCreateRequest';
export * from './storageCreateResponse';
export * from './storageDeleteRequest';
export * from './storageDeleteResponse';
export * from './storageGetAllRequest';
export * from './storageGetAllResponse';

View File

@ -0,0 +1,38 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.12
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { Credentials } from './credentials';
export interface Storage {
/**
* Entry ID
*/
id?: string;
/**
* Entry name
*/
name?: string;
/**
* Company ID
*/
company?: string;
/**
* Entry creation time
*/
created?: Date;
/**
* Storage URI
*/
uri?: string;
credentials?: Credentials;
}

View File

@ -0,0 +1,30 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.12
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { Credentials } from './credentials';
export interface StorageCreateRequest {
/**
* Storage name
*/
name?: string;
/**
* Storage URI
*/
uri: string;
credentials?: Credentials;
/**
* Company under which to add this storage. Only valid for users with the root or system role, otherwise the calling user's company will be used.
*/
company?: string;
}

View File

@ -0,0 +1,20 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.12
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface StorageCreateResponse {
/**
* New storage ID
*/
id?: string;
}

View File

@ -0,0 +1,20 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.12
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface StorageDeleteRequest {
/**
* Storage entry ID
*/
storage: string;
}

View File

@ -0,0 +1,20 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.12
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface StorageDeleteResponse {
/**
* Number of storage entries deleted (0 or 1)
*/
deleted?: number;
}

View File

@ -0,0 +1,40 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.12
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface StorageGetAllRequest {
/**
* Get only storage entries whose name matches this pattern (python regular expression syntax)
*/
name?: string;
/**
* List of Storage IDs used to filter results
*/
id?: Array<string>;
/**
* Page number, returns a specific page out of the result list of results.
*/
page?: number;
/**
* Page size, specifies the number of results returned in each page (last page may contain fewer results)
*/
page_size?: number;
/**
* List of field names to order by. When search_text is used, '@text_score' can be used as a field representing the text score of returned documents. Use '-' prefix to specify descending order. Optional, recommended when using page
*/
order_by?: Array<string>;
/**
* List of document field names (nesting is supported using '.', e.g. execution.model_labels). If provided, this list defines the query's projection (only these fields will be returned for each result entry)
*/
only_fields?: Array<string>;
}

View File

@ -0,0 +1,21 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 2.12
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { Storage } from './storage';
export interface StorageGetAllResponse {
/**
* Storage entries list
*/
results?: Array<Storage>;
}

View File

@ -0,0 +1,26 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { Google } from '././google';
import { Azure } from '././azure';
import { Aws } from '././aws';
export interface StorageGetSettingsResponse {
/**
* Settings last update time (UTC)
*/
last_update?: string;
aws?: Aws;
google?: Google;
azure?: Azure;
}

View File

@ -0,0 +1,28 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface StorageResetSettingsRequest {
/**
* The names of the settings to delete
*/
keys?: Array<StorageResetSettingsRequest.KeysEnum>;
}
export namespace StorageResetSettingsRequest {
export type KeysEnum = 'azure' | 'aws' | 'google';
export const KeysEnum = {
Azure: 'azure' as KeysEnum,
Aws: 'aws' as KeysEnum,
Google: 'google' as KeysEnum
}
}

View File

@ -0,0 +1,20 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface StorageResetSettingsResponse {
/**
* Number of settings documents updated (0 or 1)
*/
updated?: number;
}

View File

@ -0,0 +1,22 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
import { Google } from '././google';
import { Azure } from '././azure';
import { Aws } from '././aws';
export interface StorageSetSettingsRequest {
aws?: Aws;
google?: Google;
azure?: Azure;
}

View File

@ -0,0 +1,20 @@
/**
* storage
* No description provided (generated by Openapi Generator https://github.com/openapitools/openapi-generator)
*
* OpenAPI spec version: 999.0
*
*
* NOTE: This class is auto generated by the swagger code generator program.
* https://github.com/swagger-api/swagger-codegen.git
* Do not edit the class manually.
*/
export interface StorageSetSettingsResponse {
/**
* Number of settings documents updated (0 or 1)
*/
updated?: number;
}

0
src/app/business-logic/services/models.service.ts Executable file → Normal file
View File

0
src/app/business-logic/services/tasks.service.ts Executable file → Normal file
View File

0
src/app/business-logic/variables.ts Executable file → Normal file
View File

View File

@ -1,12 +1,14 @@
import {LoginService} from '~/shared/services/login.service';
import {ConfigurationService} from '@common/shared/services/configuration.service';
import {switchMap} from 'rxjs';
import {combineLatest} from 'rxjs';
export const loadUserAndPreferences = (
loginService: LoginService,
confService: ConfigurationService,
): () => Promise<any> => (): Promise<any> => new Promise((resolve) => {
confService.initConfigurationService()
.pipe(switchMap(() => loginService.initCredentials()))
combineLatest([
confService.initConfigurationService(),
loginService.initCredentials()
])
.subscribe(() => loginService.loginFlow(resolve));
});

2
src/app/core/core.module.ts Executable file → Normal file
View File

@ -96,7 +96,7 @@ const userPrefMetaFactory = (userPreferences: UserPreferences): MetaReducer[] =>
(reducer: ActionReducer<any>) =>
createUserPrefReducer('rootProjects', ['tagsColors', 'graphVariant', 'showHidden', 'hideExamples', 'defaultNestedModeForFeature', 'blockUserScript'], [ROOT_PROJECTS_PREFIX], userPreferences, reducer),
(reducer: ActionReducer<any>) =>
createUserPrefReducer('views', ['autoRefresh', 'neverShowPopupAgain', 'redactedArguments', 'hideRedactedArguments'], [VIEW_PREFIX], userPreferences, reducer),
createUserPrefReducer('views', ['autoRefresh', 'neverShowPopupAgain', 'redactedArguments', 'hideRedactedArguments', 'theme', 'hideEnterpriseFeatures'], [VIEW_PREFIX], userPreferences, reducer),
localStorageReducer,
(reducer: ActionReducer<any>) =>
createUserPrefReducer('projects', projectSyncedKeys, [PROJECTS_PREFIX], userPreferences, reducer),

0
src/app/core/effects/users.effects.ts Executable file → Normal file
View File

0
src/app/core/models/actions.ts Executable file → Normal file
View File

0
src/app/core/models/model-data.ts Executable file → Normal file
View File

View File

@ -1,7 +1,7 @@
import {createSelector} from '@ngrx/store';
import {ActionCreator, createReducer, createSelector, on, ReducerTypes} from '@ngrx/store';
import {
initViewState as commonInitState,
viewReducer as commonViewReducer,
viewReducers,
ViewState as CommonViewState
} from '@common/core/reducers/view.reducer';
import {dismissSurvey} from '../actions/layout.actions';
@ -16,7 +16,7 @@ interface ViewState extends CommonViewState {
const initViewState: ViewState = {
...commonInitState,
availableUpdates : null,
showSurvey: true
showSurvey: true,
};
export const views = state => state.views as ViewState;
@ -27,14 +27,11 @@ export const selectActiveWorkspaceReady = createSelector(views, (state) => true)
export const selectProjectType = createSelector(selectRouterConfig,
config => (config && routeConfToProjectType(config)) ?? 'datasets');
export function viewReducer(viewState: ViewState = initViewState, action) {
switch (action.type) {
case setServerUpdatesAvailable.type:
return {...viewState, availableUpdates: (action as ReturnType<typeof setServerUpdatesAvailable>).availableUpdates};
case dismissSurvey.type:
return {...viewState, showSurvey: false};
default:
return commonViewReducer(viewState, action);
}
}
export const viewReducer = createReducer(
initViewState,
on(setServerUpdatesAvailable, (state, action): ViewState =>
({...state, availableUpdates: action.availableUpdates})),
on(dismissSurvey, (state): ViewState =>
({...state, showSurvey: false})),
...viewReducers as unknown as ReducerTypes<ViewState, ActionCreator[]>[]
);

View File

@ -8,7 +8,7 @@
(openDatasetSelected)="openDatasetCardClicked($event)"
(loadMoreClicked)="loadMore()"
[projectsList]="projectsResults$ | async"
[experimentsList]="experimentsResults$ | async"
[tasksList]="experimentsResults$ | async"
[modelsList]="modelsResults$ | async"
[datasetsList]="datasetsResults$ | async"
[pipelinesList]="pipelinesResults$ | async"

View File

@ -1,30 +1,28 @@
<div class="search-container">
<div class="d-flex-center tabs">
<div class="ps-3 py-3">
@for (searchTab of activeLinksList; track searchTab.name) {
<span
class="pointer category-link"
[class.active]="activeLink === searchTab.name"
[tabindex]="$count"
(click)="activeLinkChanged.emit(searchTab.name)"
(keyup)="activeLinkChanged.emit(searchTab.name)"
>{{searchTab.label}} ({{resultsCount?.[searchTab.name]}})</span>
}
</div>
</div>
<div class="h-100">
<mat-tab-group
[selectedIndex]="activeIndex()"
(selectedTabChange)="activeLinkChanged.emit(activeLinksList[$event.index].name)"
[mat-stretch-tabs]="false" mat-align-tabs="center">
@for (searchTab of activeLinksList; track searchTab.name) {
<mat-tab
[label]="searchTab.label + (resultsCount()?.[searchTab.name] !== null ? ' (' + resultsCount()?.[searchTab.name] + ')' : '')"
class="tab-link"
></mat-tab>
}
</mat-tab-group>
<div class="page-container">
<sm-virtual-grid
[cardTemplate]="
activeLink === searchPages.projects ? ProjectTemplate :
activeLink === searchPages.experiments ? ExperimentTemplate :
activeLink === searchPages.models ? ModelsTemplate :
activeLink === searchPages.openDatasets ? openDatasetTemplate :
activeLink === searchPages.pipelines ? PipelineTemplate :
activeLink === searchPages.reports ? ReportsTemplate :
activeLink() === searchPages.projects ? ProjectTemplate :
activeLink() === searchPages.experiments ? ExperimentTemplate :
activeLink() === searchPages.models ? ModelsTemplate :
activeLink() === searchPages.openDatasets ? openDatasetTemplate :
activeLink() === searchPages.pipelines ? PipelineTemplate :
activeLink() === searchPages.reports ? ReportsTemplate :
ProjectTemplate"
[items]="getResults()"
[cardHeight]="getCardHeight()"
[showLoadMoreButton]="getResults().length < resultsCount?.[activeLink]"
[showLoadMoreButton]="getResults().length < resultsCount()?.[activeLink()] && !loading()"
(itemClicked)="projectClicked($event)"
(loadMoreClicked)="loadMoreClicked.emit()"
>
@ -62,11 +60,11 @@
</ng-template>
<ng-template #openDatasetTemplate let-dataset>
<sm-simple-dataset-card
<sm-open-dataset-card
[hideMenu]="true"
[project]="dataset"
(projectCardClicked)="openDatasetClicked($event)"
></sm-simple-dataset-card>
></sm-open-dataset-card>
</ng-template>
<ng-template #ReportsTemplate let-report>
<sm-report-card
@ -75,5 +73,4 @@
(cardClicked)="reportClicked($event)"
></sm-report-card>
</ng-template>
</div>

View File

@ -1,24 +1,8 @@
@import "../../../../webapp-common/shared/ui-components/styles/variables";
@import "../../../../webapp-common/shared/ui-components/styles/mixins/common";
.category-link{
padding-right: 24px;
color: $blue-300;
&:hover {
color: $blue-200;
}
&.active {
color: $neon-yellow;
}
}
.search-container{
height: calc(100% - 24px);
.tabs {
@include recent-title();
}
.mat-mdc-tab-group {
--mat-tab-header-label-text-size: 14px;
}
.page-container {
height: calc(100% - 35px);
height: calc(100% - 67px);
margin-top: 18px;
}

View File

@ -1,4 +1,4 @@
import {Component, EventEmitter, Input, Output} from '@angular/core';
import {Component, output, input, computed} from '@angular/core';
import {Project} from '~/business-logic/model/projects/project';
import {Task} from '~/business-logic/model/tasks/task';
import {ITask} from '~/business-logic/model/al-task';
@ -12,26 +12,31 @@ import {IReport} from '@common/reports/reports.consts';
styleUrls: ['./search-results-page.component.scss']
})
export class SearchResultsPageComponent {
public searchPages = activeSearchLink;
public activeLinksList = activeLinksList;
protected readonly searchPages = activeSearchLink;
protected readonly activeLinksList = activeLinksList;
@Input() projectsList: Array<Project> = [];
@Input() experimentsList: Array<Task> = [];
@Input() modelsList: Array<Model> = [];
@Input() pipelinesList: Array<Project> = [];
@Input() datasetsList: Array<Project> = [];
@Input() reportsList: Array<IReport> = [];
@Input() activeLink: ActiveSearchLink;
@Input() resultsCount: Map<ActiveSearchLink, number>;
@Output() projectSelected = new EventEmitter<Project>();
@Output() activeLinkChanged = new EventEmitter<string>();
@Output() experimentSelected = new EventEmitter<ITask>();
@Output() modelSelected = new EventEmitter<Model>();
@Output() pipelineSelected = new EventEmitter<Project>();
@Output() reportSelected = new EventEmitter<IReport>();
@Output() openDatasetSelected = new EventEmitter<Project>();
@Output() loadMoreClicked = new EventEmitter();
projectsList = input<Project[]>([]);
datasetsList = input<Project[]>([]);
tasksList = input<Task[]>([]);
modelsList = input<Model[]>([]);
pipelinesList = input<Project[]>([]);
reportsList = input<IReport[]>([]);
activeLink = input<ActiveSearchLink>();
resultsCount = input<Map<ActiveSearchLink, number>>();
projectSelected = output<Project>();
activeLinkChanged = output<string>();
openDatasetSelected = output<Project>();
experimentSelected = output<ITask>();
modelSelected = output<Model>();
reportSelected = output<IReport>();
pipelineSelected = output<Project>();
loadMoreClicked = output();
protected loading = computed<boolean>(() => [null, undefined].includes(this.resultsCount()?.[this.activeLink()]));
protected getResults = computed(() => this[`${this.activeLink()}List`]());
activeIndex = computed<number>(() => activeLinksList.findIndex(item => item.name === this.activeLink()));
public projectClicked(project: Project) {
this.projectSelected.emit(project);
@ -57,10 +62,9 @@ export class SearchResultsPageComponent {
this.reportSelected.emit(report);
}
getResults = () => this[`${this.activeLink}List`];
getCardHeight() {
switch (this.activeLink) {
switch (this.activeLink()) {
case activeSearchLink.projects:
return 246;
case activeSearchLink.experiments:

View File

@ -4,7 +4,7 @@ import {CrumbTypeEnum} from '@common/layout/breadcrumbs/breadcrumbs.component';
import {DashboardSearchComponent} from '~/features/dashboard-search/containers/dashboard-search/dashboard-search.component';
const staticBreadcrumb = [[{
name: 'DASHBOARD',
name: 'GLOBAL SEARCH',
type: CrumbTypeEnum.Feature
}]];

View File

@ -1,8 +1,8 @@
export type ActiveSearchLink = 'projects' | 'experiments' | 'models' | 'pipelines' | 'datasets';
export type ActiveSearchLink = 'projects' | 'tasks' | 'models' | 'pipelines' | 'datasets';
export const activeSearchLink = {
projects: 'projects' as ActiveSearchLink,
experiments: 'experiments' as ActiveSearchLink,
experiments: 'tasks' as ActiveSearchLink,
models: 'models' as ActiveSearchLink,
pipelines: 'pipelines' as ActiveSearchLink,
openDatasets: 'datasets' as ActiveSearchLink,
@ -19,7 +19,7 @@ export const activeLinksList = [
name: activeSearchLink.openDatasets,
},
{
label: 'EXPERIMENTS',
label: 'TASKS',
name: activeSearchLink.experiments,
},
{

View File

@ -18,23 +18,22 @@ export class DashboardSearchEffects {
) {}
getResultsCount = createEffect(() => this.actions.pipe(
ofType(getResultsCount),
withLatestFrom(
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser),
this.store.select(selectShowHidden),
this.store.select(selectHideExamples),
),
switchMap(([action, userFocus, user, hidden, hideExamples]) => this.organizationApi.organizationGetEntitiesCount({
/* eslint-disable @typescript-eslint/naming-convention */
...(userFocus && {active_users: [user.id]}),
...(hidden && {search_hidden: true}),
...(hideExamples && {allow_public: false}),
...getEntityStatQuery(action, hidden)
/* eslint-enable @typescript-eslint/naming-convention */
})),
map(({tasks: experiments, ...rest}) =>
setResultsCount({counts: {...rest, experiments}}))
));
ofType(getResultsCount),
withLatestFrom(
this.store.select(selectShowOnlyUserWork),
this.store.select(selectCurrentUser),
this.store.select(selectShowHidden),
this.store.select(selectHideExamples),
),
switchMap(([action, userFocus, user, hidden, hideExamples]) => this.organizationApi.organizationGetEntitiesCount({
/* eslint-disable @typescript-eslint/naming-convention */
...(userFocus && {active_users: [user.id]}),
...(hidden && {search_hidden: true}),
...(hideExamples && {allow_public: false}),
...getEntityStatQuery(action, hidden)
/* eslint-enable @typescript-eslint/naming-convention */
})),
map(res => setResultsCount({counts: res}))
)
);
}

View File

@ -16,6 +16,7 @@ import {DashboardSearchComponent} from '~/features/dashboard-search/containers/d
import {DatasetsSharedModule} from '~/features/datasets/shared/datasets-shared.module';
import {ReportCardComponent} from '@common/reports/report-card/report-card.component';
import {DashboardSearchRoutingModule} from '~/features/dashboard-search/dashboard-search-routing.module';
import {MatTab, MatTabGroup} from '@angular/material/tabs';
@NgModule({
imports: [
@ -31,6 +32,8 @@ import {DashboardSearchRoutingModule} from '~/features/dashboard-search/dashboar
PipelineCardComponent,
DatasetsSharedModule,
ReportCardComponent,
MatTabGroup,
MatTab,
],
declarations:[
SearchResultsPageComponent, DashboardSearchComponent

9
src/app/features/dashboard/dashboard-routing.module.ts Executable file → Normal file
View File

@ -4,18 +4,19 @@ import {DashboardComponent} from './dashboard.component';
import {CrumbTypeEnum} from '@common/layout/breadcrumbs/breadcrumbs.component';
const staticBreadcrumb = [[{
name: 'DASHBOARD',
type: CrumbTypeEnum.Feature
name: 'PROJECTS DASHBOARD',
type: CrumbTypeEnum.Feature
}]];
export const routes: Routes = [
{path: '', component: DashboardComponent, data: {staticBreadcrumb}},
{
path: 'search',
loadChildren: () => import('~/features/dashboard-search/dashboard-search.module').then(m => m.DashboardSearchModule),
data: {staticBreadcrumb}}
loadChildren: () => import('~/features/dashboard-search/dashboard-search.module').then(m => m.DashboardSearchModule)
}
];
@NgModule({
imports: [
RouterModule.forChild(routes)

10
src/app/features/dashboard/dashboard.component.html Executable file → Normal file
View File

@ -1,16 +1,16 @@
<div class="dashboard-body">
<div class="recent">
<sm-dashboard-projects (width)="setWidth($event)"></sm-dashboard-projects>
<sm-dashboard-reports></sm-dashboard-reports>
<sm-dashboard-experiments
[style.width]="width && width > 900 ? width + 'px' : '100%'"
[recentTasks]="recentTasks$| async"
>
<div header-buttons>
<button
*smCheckPermission="true"
class="btn btn-cml-primary d-flex align-items-center"
(click)="redirectToWorkers()"
><i class="al-icon al-ico-queues al-color light-grey-blue sm me-2"></i>MANAGE WORKERS AND QUEUES</button>
<button mat-flat-button data-id="Manage Workers And Queues" (click)="redirectToWorkers()">
<mat-icon fontSet="al" fontIcon="al-ico-queues"></mat-icon>
MANAGE WORKERS AND QUEUES
</button>
</div>
</sm-dashboard-experiments>
</div>

11
src/app/features/dashboard/dashboard.component.scss Executable file → Normal file
View File

@ -1,16 +1,14 @@
@import "../../webapp-common/shared/ui-components/styles/mixins/common";
@import "../../webapp-common/shared/ui-components/styles/variables";
@import "mixins/common";
@import "variables";
:host {
.dashboard-body {
height: 100%;
padding: 0 24px;
overflow: auto;
}
.recent {
height: 100%;
overflow: auto;
max-width: 2400px;
margin: 0 auto;
}
@ -28,8 +26,9 @@
sm-dashboard-experiments {
display: block;
margin: 24px auto 0;
height: calc(100% - 356px);
padding-top: 24px;
margin: 0 auto;
height: calc(100% - 620px);
}
.dashboard-search {

3
src/app/features/dashboard/dashboard.component.ts Executable file → Normal file
View File

@ -5,7 +5,7 @@ import {ActivatedRoute, Router} from '@angular/router';
import {selectCurrentUser, selectShowOnlyUserWork} from '@common/core/reducers/users-reducer';
import {debounceTime, filter, take} from 'rxjs/operators';
import {setDeep} from '@common/core/actions/projects.actions';
import {getRecentProjects, getRecentExperiments} from '@common/dashboard/common-dashboard.actions';
import {getRecentProjects, getRecentExperiments, getRecentReports} from '@common/dashboard/common-dashboard.actions';
import {selectFirstLogin} from '@common/core/reducers/view.reducer';
import {MatDialog} from '@angular/material/dialog';
import {WelcomeMessageComponent} from '@common/layout/welcome-message/welcome-message.component';
@ -52,6 +52,7 @@ export class DashboardComponent implements OnInit, OnDestroy {
.subscribe(() => {
this.store.dispatch(getRecentProjects());
this.store.dispatch(getRecentExperiments());
this.store.dispatch(getRecentReports());
});
this.welcomeSub = this.store.select(selectFirstLogin)

13
src/app/features/dashboard/dashboard.module.ts Executable file → Normal file
View File

@ -4,7 +4,6 @@ import {DashboardComponent} from './dashboard.component';
import {ExperimentSharedModule} from '../experiments/shared/experiment-shared.module';
import {DashboardRoutingModule} from './dashboard-routing.module';
import {StoreModule} from '@ngrx/store';
import {GettingStartedCardComponent} from './dumb/getting-started-card/getting-started-card.component';
import {CommonDashboardModule} from '@common/dashboard/common-dashboard.module';
import {commonDashboardReducer} from '@common/dashboard/common-dashboard.reducer';
import {SharedModule} from '~/shared/shared.module';
@ -14,13 +13,15 @@ import {DatasetsSharedModule} from '~/features/datasets/shared/datasets-shared.m
import {ScrollingModule} from '@angular/cdk/scrolling';
import {CheckPermissionDirective} from '~/shared/directives/check-permission.directive';
import {PlusCardComponent} from '@common/shared/ui-components/panel/plus-card/plus-card.component';
import {NeonButtonComponent} from '@common/shared/ui-components/buttons/neon-button/neon-button.component';
import {OverflowsDirective} from '@common/shared/ui-components/directives/overflows.directive';
import {PipelineCardComponent} from '@common/pipelines/pipeline-card/pipeline-card.component';
import {ModelCardComponent} from '@common/shared/ui-components/panel/model-card/model-card.component';
import {ExperimentCardComponent} from '@common/shared/ui-components/panel/experiment-card/experiment-card.component';
import {ProjectCardComponent} from '@common/shared/ui-components/panel/project-card/project-card.component';
import {VirtualGridComponent} from '@common/shared/components/virtual-grid/virtual-grid.component';
import {MatIcon} from '@angular/material/icon';
import {MatButton} from '@angular/material/button';
import {DashboardReportsComponent} from '@common/dashboard/containers/dashboard-reports/dashboard-reports.component';
@NgModule({
imports: [
@ -36,15 +37,17 @@ import {VirtualGridComponent} from '@common/shared/components/virtual-grid/virtu
ScrollingModule,
CheckPermissionDirective,
PlusCardComponent,
NeonButtonComponent,
OverflowsDirective,
PipelineCardComponent,
ModelCardComponent,
ExperimentCardComponent,
ProjectCardComponent,
VirtualGridComponent
VirtualGridComponent,
MatIcon,
MatButton,
DashboardReportsComponent
],
declarations : [DashboardComponent, GettingStartedCardComponent]
declarations : [DashboardComponent]
})
export class DashboardModule {
}

View File

@ -1,9 +0,0 @@
<div class="no-data-box">
<img [src]="'../../../assets/icons/empty-data/'+ icon"
class="main-image">
<div class="empty-label">{{title}}</div>
<div class="empty-sub-label line-breaker">{{subtitle}}
</div>
<sm-neon-button [label]="buttonLabel">
</sm-neon-button>
</div>

View File

@ -1,34 +0,0 @@
@import "../../../../webapp-common/shared/ui-components/styles/variables";
.no-data-box {
text-align: center;
padding: 32px;
margin-top: 15px;
margin-bottom: 15px;
height: 359px;
border-radius: 6px;
background-color: $blue-800;
}
.main-image {
margin: 15px;
width: 100px;
height: 100px;
object-fit: contain;
}
.empty-label{
padding: 15px;
font-size: 22px;
font-weight: bold;
color: $blue-100;
}
.empty-sub-label{
padding: 15px;
font-size: 16px;
line-height: 1.31;
color: $blue-300;
}
.line-breaker {
white-space: pre-line;
}

View File

@ -1,20 +0,0 @@
import {Component, EventEmitter, Input, OnInit, Output} from '@angular/core';
@Component({
selector: 'sm-getting-started-card',
templateUrl: './getting-started-card.component.html',
styleUrls: ['./getting-started-card.component.scss']
})
export class GettingStartedCardComponent implements OnInit {
@Input() icon;
@Input() title;
@Input() subtitle;
@Input() buttonLabel;
@Output() buttonClicked = new EventEmitter();
constructor() { }
ngOnInit() {
}
}

View File

@ -1,14 +1,14 @@
import {NgModule} from '@angular/core';
import {RouterModule, Routes} from '@angular/router';
import {SimpleDatasetsComponent} from '@common/datasets/simple-datasets/simple-datasets.component';
import {EntityTypeEnum} from '~/shared/constants/non-common-consts';
import {CrumbTypeEnum} from '@common/layout/breadcrumbs/breadcrumbs.component';
import {NestedSimpleDatasetsPageComponent} from '@common/datasets/nested-simple-datasets-page/nested-simple-datasets-page.component';
import {OpenDatasetsComponent} from '@common/datasets/open-datasets/open-datasets.component';
import {NestedOpenDatasetsPageComponent} from '@common/datasets/nested-open-datasets-page/nested-open-datasets-page.component';
const routes: Routes = [
{
path : '',
component: SimpleDatasetsComponent,
component: OpenDatasetsComponent,
data: {search: true, staticBreadcrumb:[[{
name: 'DATASETS',
type: CrumbTypeEnum.Feature
@ -20,21 +20,22 @@ const routes: Routes = [
children: [
{
path: 'datasets',
component: SimpleDatasetsComponent,
component: OpenDatasetsComponent,
data: {search: true}
},
{
path: 'projects',
component: NestedSimpleDatasetsPageComponent,
component: NestedOpenDatasetsPageComponent,
data: {search: true}
},
{path: 'experiments', redirectTo: 'tasks'},
{
path: 'experiments',
path: 'tasks',
loadChildren: () => import('@common/dataset-version/dataset-version.module')
.then(m => m.DatasetVersionModule)
},
{
path: 'compare-experiments',
path: 'compare-tasks',
data: {entityType: EntityTypeEnum.dataset},
loadChildren: () => import('@common/experiments-compare/experiments-compare.module').then(m => m.ExperimentsCompareModule)
},

View File

@ -1,15 +1,16 @@
import {CommonModule} from '@angular/common';
import {NgModule, NO_ERRORS_SCHEMA} from '@angular/core';
import {SimpleDatasetsComponent} from '@common/datasets/simple-datasets/simple-datasets.component';
import {CommonProjectsModule} from '@common/projects/common-projects.module';
import {DatasetsRoutingModule} from '~/features/datasets/datasets-routing.module';
import {DatasetsSharedModule} from '~/features/datasets/shared/datasets-shared.module';
import {NestedDatasetsPageComponent} from '~/features/datasets/nested-datasets-page/nested-datasets-page.component';
import {ProjectsSharedModule} from '~/features/projects/shared/projects-shared.module';
import {LabeledFormFieldDirective} from '@common/shared/directive/labeled-form-field.directive';
import {ButtonToggleComponent} from '@common/shared/ui-components/inputs/button-toggle/button-toggle.component';
import {ShowTooltipIfEllipsisDirective} from '@common/shared/ui-components/indicators/tooltip/show-tooltip-if-ellipsis.directive';
import {PushPipe} from '@ngrx/component';
import {OpenDatasetsComponent} from '@common/datasets/open-datasets/open-datasets.component';
import {MatButton} from '@angular/material/button';
import {MatIcon} from '@angular/material/icon';
import {DotsLoadMoreComponent} from '@common/shared/ui-components/indicators/dots-load-more/dots-load-more.component';
@ -21,14 +22,15 @@ import {DotsLoadMoreComponent} from '@common/shared/ui-components/indicators/dot
DatasetsSharedModule,
NestedDatasetsPageComponent,
ProjectsSharedModule,
LabeledFormFieldDirective,
ButtonToggleComponent,
ShowTooltipIfEllipsisDirective,
PushPipe,
MatButton,
MatIcon,
DotsLoadMoreComponent
],
declarations: [
SimpleDatasetsComponent,
OpenDatasetsComponent,
],
schemas: [NO_ERRORS_SCHEMA],
exports: []

View File

@ -12,24 +12,27 @@
(orderByChanged)="orderByChanged($event)"
(loadMore)="loadMore()"
(toggleNestedView)="toggleNestedView($event)"
>
>
<button create-button
class="btn btn-cml-primary d-flex align-items-center"
(click)="createExamples()">
<i class="al-icon al-ico-add sm me-2"></i>NEW DATASET
mat-flat-button
(click)="createExamples()">
<mat-icon fontSet="al" fontIcon="al-ico-add"></mat-icon>
NEW DATASET
</button>
<div empty-state class="empty-datasets">
<div class="title-icon"><i class="al-icon al-ico-datasets xxl"></i></div>
<div class="title">NO DATASETS TO SHOW</div>
<div class="sub-title">Run your first dataset to see it displayed here
<ng-container *ngIf="allExamples"> or <a href="" (click)="$event.preventDefault(); createExamples()" class="link">generate
@if (allExamples) {
or <a href="" (click)="$event.preventDefault(); createExamples()" class="link">generate
example
</a></ng-container>
</div>
<sm-dataset-empty [showButton]="true"></sm-dataset-empty>
</a>
}
</div>
<sm-dataset-empty [showButton]="true"></sm-dataset-empty>
</div>
</sm-nested-project-view-page>
@ -37,21 +40,21 @@
<sm-circle-counter
[counter]="project.id === '*' ? '∞' : project.stats?.datasets?.count ?? '0'"
[label]="'DATASETS'"
[type]="project.stats?.datasets?.count===0 ? circleTypeEnum.empty : circleTypeEnum.pending"></sm-circle-counter>
[type]="project.stats?.datasets?.count===0 ? circleTypeEnum.empty : circleTypeEnum.pending"></sm-circle-counter>
<sm-circle-counter label="TAGS IN USE" [counter]="[]">
<sm-tag-list
*ngIf="!hideMenu; else: ReadOnlyTags"
class="tags-list-counter"
[readonly]="true"
[class.empty-tags]="!(project.stats?.datasets?.tags.length > 0)"
tagsList
[tags]="project.stats?.datasets?.tags"
smClickStopPropagation
></sm-tag-list>
@if (!hideMenu) {
<sm-tag-list
class="tags-list-counter"
[readonly]="true"
[class.empty-tags]="!(project.stats?.datasets?.tags.length > 0)"
tagsList
[tags]="project.stats?.datasets?.tags"
smClickStopPropagation
></sm-tag-list>
} @else {
<sm-tag-list [tags]="project.tags"></sm-tag-list>
}
</sm-circle-counter>
<ng-template #ReadOnlyTags>
<sm-tag-list [tags]="project.tags"></sm-tag-list>
</ng-template>
</ng-template>

View File

@ -2,26 +2,31 @@ import {Component} from '@angular/core';
import {ProjectTypeEnum} from '@common/nested-project-view/nested-project-view-page/nested-project-view-page.component';
import {CircleTypeEnum} from '~/shared/constants/non-common-consts';
import {ProjectsSharedModule} from '~/features/projects/shared/projects-shared.module';
import {AsyncPipe, NgIf} from '@angular/common';
import { AsyncPipe } from '@angular/common';
import {CommonProjectsPageComponent} from '@common/projects/containers/projects-page/common-projects-page.component';
import {DatasetEmptyComponent} from '@common/datasets/dataset-empty/dataset-empty.component';
import {CircleCounterComponent} from '@common/shared/ui-components/indicators/circle-counter/circle-counter.component';
import {TagListComponent} from '@common/shared/ui-components/tags/tag-list/tag-list.component';
import {MatButton} from '@angular/material/button';
import {MatIcon} from '@angular/material/icon';
import {ClickStopPropagationDirective} from '@common/shared/ui-components/directives/click-stop-propagation.directive';
@Component({
selector: 'sm-nested-datasets-page',
templateUrl: './nested-datasets-page.component.html',
styleUrls: [
'../../../webapp-common/nested-project-view/nested-project-view-page/nested-project-view-page.component.scss',
'../../../webapp-common/datasets/simple-datasets/simple-datasets.component.scss'
'../../../webapp-common/datasets/open-datasets/open-datasets.component.scss'
],
imports: [
ProjectsSharedModule,
AsyncPipe,
NgIf,
CircleCounterComponent,
TagListComponent
],
TagListComponent,
MatButton,
MatIcon,
ClickStopPropagationDirective
],
standalone: true
})
export class NestedDatasetsPageComponent extends CommonProjectsPageComponent {

View File

@ -2,7 +2,6 @@ import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {SharedModule} from '~/shared/shared.module';
import {SimpleDatasetCardComponent} from '@common/datasets/simple-dataset-card/simple-dataset-card.component';
import {ProjectsSharedModule} from '~/features/projects/shared/projects-shared.module';
import {ClickStopPropagationDirective} from '@common/shared/ui-components/directives/click-stop-propagation.directive';
import {TagListComponent} from '@common/shared/ui-components/tags/tag-list/tag-list.component';
@ -16,9 +15,10 @@ import {NAPipe} from '@common/shared/pipes/na.pipe';
import {FileSizePipe} from '@common/shared/pipes/filesize.pipe';
import {TooltipDirective} from '@common/shared/ui-components/indicators/tooltip/tooltip.directive';
import {ShowTooltipIfEllipsisDirective} from '@common/shared/ui-components/indicators/tooltip/show-tooltip-if-ellipsis.directive';
import {OpenDatasetCardComponent} from '@common/datasets/open-dataset-card/open-dataset-card.component';
const _declerations = [
SimpleDatasetCardComponent
OpenDatasetCardComponent
];
@NgModule({

View File

View File

View File

@ -4,7 +4,7 @@
border-bottom: 1px solid #efefef;
&.minimized {
grid-template-columns: 0 1fr 90px;
grid-template-columns: 0 1fr 108px;
}
@media(max-width: 1200px) {

View File

@ -1,4 +1,4 @@
import {Component, computed} from '@angular/core';
import {Component, computed, Signal} from '@angular/core';
import {ExperimentMenuComponent} from '@common/experiments/shared/components/experiment-menu/experiment-menu.component';
@Component({

View File

@ -23,20 +23,34 @@
<span class="refresh-position" refresh>
<div class="scalar-buttons">
@if (routerConfig.includes('scalar')) {
<div class="table-graph-toggle" [class.maximized]="!minimized">
<i class="al-icon pointer al-color blue-400"
[smTooltip]="(metricValuesView$ | async)===false? 'Metric values': 'Graphs'"
[class]="(metricValuesView$ | async)===false? 'al-ico-table-view': 'al-ico-charts-view'"
(click)="toggleTableView()"
></i>
</div>
<button mat-icon-button class="table-graph-toggle"
[smTooltip]="(metricValuesView$ | async)===false? 'Metric values': 'Graphs'"
[class.maximized]="!minimized"
(click)="toggleTableView()">
<mat-icon fontSet="al" [fontIcon]="(metricValuesView$ | async)===false? 'al-ico-table-view': 'al-ico-charts-view'"></mat-icon>
</button>
}
@if ((metricValuesView$ | async) === false && routerConfig.includes('scalar') && minimized) {
<sm-experiment-settings
[class.maximized]="!minimized"
[showSettings]="routerConfig.includes('scalar') && minimized"
(toggleSettings)="toggleSettingsBar()"
></sm-experiment-settings>
<button mat-icon-button [matMenuTriggerFor]="graphSettingsMenu" smTooltip="Settings">
<mat-icon fontSet="al" fontIcon="al-ico-settings"></mat-icon>
</button>
<mat-menu #graphSettingsMenu="matMenu">
<sm-graph-settings-bar
class="graph-settings-menu"
(click)="$event.stopPropagation()"
[verticalLayout]="true"
[smoothWeight]="smoothWeight$ | ngrxPush"
[smoothType]="smoothType$ | ngrxPush"
[xAxisType]="xAxisType$ | ngrxPush"
[groupBy]="groupBy$ | ngrxPush"
[groupByOptions]="groupByOptions"
(changeWeight)="changeSmoothness($event)"
(changeXAxisType)="changeXAxisType($event)"
(changeGroupBy)="changeGroupBy($event)"
(changeSmoothType)="changeSmoothType($event)"
></sm-graph-settings-bar>
</mat-menu>
}
</div>
@if (!minimized){

View File

0
src/app/features/experiments/experiments.consts.ts Executable file → Normal file
View File

139
src/app/features/experiments/experiments.module.ts Executable file → Normal file
View File

@ -10,7 +10,6 @@ import {LayoutModule} from '~/layout/layout.module';
import {ExperimentGraphsModule} from '@common/shared/experiment-graphs/experiment-graphs.module';
import {ExperimentCompareSharedModule} from '@common/experiments-compare/shared/experiment-compare-shared.module';
import {AngularSplitModule} from 'angular-split';
import {CommonLayoutModule} from '@common/layout/layout.module';
import {DebugImagesModule} from '@common/debug-images/debug-images.module';
import {
ExperimentInfoExecutionComponent
@ -71,7 +70,6 @@ import {
import {ExperimentOutputLogModule} from '@common/experiments/shared/experiment-output-log/experiment-output-log.module';
import {RouterModule} from '@angular/router';
import {ScrollingModule} from '@angular/cdk/scrolling';
import {CommonDeleteDialogModule} from '@common/shared/entity-page/entity-delete/common-delete-dialog.module';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {MatRadioModule} from '@angular/material/radio';
import {SharedModule} from '~/shared/shared.module';
@ -81,7 +79,6 @@ import {Overlay} from '@angular/cdk/overlay';
import {ExperimentsComponent} from '@common/experiments/experiments.component';
import {RouterTabNavBarComponent} from '@common/shared/components/router-tab-nav-bar/router-tab-nav-bar.component';
import {MatTabsModule} from '@angular/material/tabs';
import {LabeledFormFieldDirective} from '@common/shared/directive/labeled-form-field.directive';
import {OverlayComponent} from '@common/shared/ui-components/overlay/overlay/overlay.component';
import {RefreshButtonComponent} from '@common/shared/components/refresh-button/refresh-button.component';
import {
@ -133,75 +130,79 @@ import {
import {
ExperimentArtifactsNavbarComponent
} from '@common/experiments/dumb/experiment-artifacts-navbar/experiment-artifacts-navbar.component';
import {CommonDeleteDialogModule} from '@common/shared/entity-page/entity-delete/common-delete-dialog.module';
import {MatIcon} from '@angular/material/icon';
import {MatButton, MatIconButton} from '@angular/material/button';
@NgModule({
imports: [
FormsModule,
LayoutModule,
ReactiveFormsModule,
CommonModule,
ExperimentRouterModule,
ExperimentSharedModule,
ExperimentGraphsModule,
SelectModelModule,
DebugImagesModule,
ExperimentCompareSharedModule,
CommonLayoutModule,
MatSidenavModule,
MatListModule,
AngularSplitModule,
ScrollingModule,
CommonDeleteDialogModule,
RouterModule,
SharedModule,
ExperimentOutputLogModule,
MatProgressSpinnerModule,
MatRadioModule,
RouterTabNavBarComponent,
MatTabsModule,
RouterTabNavBarComponent,
LabeledFormFieldDirective,
OverlayComponent,
RefreshButtonComponent,
InfoHeaderStatusIconLabelComponent,
NAPipe,
SortPipe,
safeAngularUrlParameterPipe,
ReplaceViaMapPipe,
FilterOutPipe,
DurationPipe,
FilterInternalPipe,
FileSizePipe,
HideRedactedArgumentsPipe,
MenuItemComponent,
CopyClipboardComponent,
SectionHeaderComponent,
InlineEditComponent,
TagsMenuComponent,
EntityFooterComponent,
MenuComponent,
ExperimentTypeIconLabelComponent,
SearchComponent,
IdBadgeComponent,
ScrollTextareaComponent,
TagListComponent,
TooltipDirective,
LabeledRowComponent,
EditableSectionComponent,
SelectableGroupedFilterListComponent,
MatMenuModule,
MatExpansionModule,
MatInputModule,
MatSelectModule,
HesitateDirective,
ShowTooltipIfEllipsisDirective,
SelectQueueModule,
PushPipe,
ExperimentHeaderComponent,
ExperimentOperationsLogComponent,
ExperimentArtifactsNavbarComponent,
],
imports: [
FormsModule,
LayoutModule,
ReactiveFormsModule,
CommonModule,
ExperimentRouterModule,
ExperimentSharedModule,
ExperimentGraphsModule,
SelectModelModule,
DebugImagesModule,
ExperimentCompareSharedModule,
MatSidenavModule,
MatListModule,
AngularSplitModule,
ScrollingModule,
RouterModule,
SharedModule,
ExperimentOutputLogModule,
MatProgressSpinnerModule,
MatRadioModule,
RouterTabNavBarComponent,
MatTabsModule,
RouterTabNavBarComponent,
OverlayComponent,
RefreshButtonComponent,
InfoHeaderStatusIconLabelComponent,
NAPipe,
SortPipe,
safeAngularUrlParameterPipe,
ReplaceViaMapPipe,
FilterOutPipe,
DurationPipe,
FilterInternalPipe,
FileSizePipe,
HideRedactedArgumentsPipe,
MenuItemComponent,
CopyClipboardComponent,
SectionHeaderComponent,
InlineEditComponent,
TagsMenuComponent,
EntityFooterComponent,
MenuComponent,
ExperimentTypeIconLabelComponent,
SearchComponent,
IdBadgeComponent,
ScrollTextareaComponent,
TagListComponent,
TooltipDirective,
LabeledRowComponent,
EditableSectionComponent,
SelectableGroupedFilterListComponent,
MatMenuModule,
MatExpansionModule,
MatInputModule,
MatSelectModule,
HesitateDirective,
ShowTooltipIfEllipsisDirective,
SelectQueueModule,
PushPipe,
ExperimentHeaderComponent,
ExperimentOperationsLogComponent,
ExperimentArtifactsNavbarComponent,
CommonDeleteDialogModule,
MatIcon,
MatButton,
MatIconButton
],
declarations: [
ExperimentsComponent,
ExperimentInfoExecutionComponent,

View File

0
src/app/features/experiments/reducers/index.ts Executable file → Normal file
View File

View File

View File

@ -23,7 +23,7 @@ import {Output} from '~/business-logic/model/tasks/output';
* an extended object of task that includes projection, will come from the server as an api response.
*/
export interface ISelectedExperiment {
id?: string;
id: string;
name?: string;
user?: User;
company?: GetCurrentUserResponseUserObjectCompany;

View File

@ -5,7 +5,6 @@ import { ExperimentMenuComponent } from '@common/experiments/shared/components/e
import {ExperimentMenuExtendedComponent} from '../containers/experiment-menu-extended/experiment-menu-extended.component';
import {ExperimentExecutionParametersComponent} from '@common/experiments/dumb/experiment-execution-parameters/experiment-execution-parameters.component';
import {CloneDialogComponent} from '@common/experiments/shared/components/clone-dialog/clone-dialog.component';
import {ExperimentSystemTagsComponent} from '@common/experiments/shared/components/experiments-system-tags/experiment-system-tags.component';
import {AbortAllChildrenDialogComponent} from '@common/experiments/shared/components/abort-all-children-dialog/abort-all-children-dialog.component';
import {ExperimentsTableComponent} from '@common/experiments/dumb/experiments-table/experiments-table.component';
import {ChangeProjectDialogComponent} from '@common/experiments/shared/components/change-project-dialog/change-project-dialog.component';
@ -16,8 +15,7 @@ import {CommonExperimentOutputEffects} from '@common/experiments/effects/common-
import {ScrollingModule} from '@angular/cdk/scrolling';
import {ExperimentsMenuEffects} from '~/features/experiments/effects/experiments-menu.effects';
import {ActionReducer, StoreConfig, StoreModule} from '@ngrx/store';
import {CommonLayoutModule} from '@common/layout/layout.module';
import {FormsModule} from '@angular/forms';
import {FormsModule, ReactiveFormsModule} from '@angular/forms';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {ExperimentOutputEffects} from '~/features/experiments/effects/experiment-output.effects';
import {EXPERIMENTS_PREFIX, EXPERIMENTS_STORE_KEY} from '@common/experiments/experiment.consts';
@ -34,7 +32,6 @@ import {EXPERIMENTS_INFO_PREFIX} from '@common/experiments/actions/common-experi
import {experimentsReducers} from '~/features/experiments/reducers';
import {CommonExperimentConverterService} from '@common/experiments/shared/services/common-experiment-converter.service';
import {HyperParamMetricColumnComponent} from '@common/experiments/shared/components/hyper-param-metric-column/hyper-param-metric-column.component';
import {LabeledFormFieldDirective} from '@common/shared/directive/labeled-form-field.directive';
import {StringIncludedInArrayPipe} from '@common/shared/pipes/string-included-in-array.pipe';
import {TimeAgoPipe} from '@common/shared/pipes/timeAgo';
import {ReplaceViaMapPipe} from '@common/shared/pipes/replaceViaMap';
@ -44,7 +41,6 @@ import {MenuItemComponent} from '@common/shared/ui-components/panel/menu-item/me
import {UniqueNameValidatorDirective} from '@common/shared/ui-components/template-forms-ui/unique-name-validator.directive';
import {TagsMenuComponent} from '@common/shared/ui-components/tags/tags-menu/tags-menu.component';
import {CustomColumnsListComponent} from '@common/shared/components/custom-columns-list/custom-columns-list.component';
import {CheckboxControlComponent} from '@common/shared/ui-components/forms/checkbox-control/checkbox-control.component';
import {MenuComponent} from '@common/shared/ui-components/panel/menu/menu.component';
import {StatusIconLabelComponent} from '@common/shared/experiment-status-icon-label/status-icon-label.component';
import {ExperimentTypeIconLabelComponent} from '@common/shared/experiment-type-icon-label/experiment-type-icon-label.component';
@ -57,9 +53,8 @@ import {TagListComponent} from '@common/shared/ui-components/tags/tag-list/tag-l
import {TagComponent} from '@common/shared/ui-components/indicators/tag/tag.component';
import {MatMenuModule} from '@angular/material/menu';
import {MatSidenavModule} from '@angular/material/sidenav';
import {TableFilterSortTemplateComponent} from '@common/shared/ui-components/data/table/table-filter-sort-template/table-filter-sort-template.component';
import {TableFilterSortComponent} from '@common/shared/ui-components/data/table/table-filter-sort/table-filter-sort.component';
import {TableComponent} from '@common/shared/ui-components/data/table/table.component';
import {TableCardFilterTemplateComponent} from '@common/shared/ui-components/data/table/table-card-filter-template/table-card-filter-template.component';
import {MatSelectModule} from '@angular/material/select';
import {ButtonToggleComponent} from '@common/shared/ui-components/inputs/button-toggle/button-toggle.component';
import {GroupedCheckedFilterListComponent} from '@common/shared/ui-components/data/grouped-checked-filter-list/grouped-checked-filter-list.component';
@ -79,6 +74,12 @@ import {ExperimentCustomColsMenuComponent} from '@common/experiments/dumb/experi
import {SelectMetricForCustomColComponent} from '@common/experiments/dumb/select-metric-for-custom-col/select-metric-for-custom-col.component';
import {SelectHyperParamsForCustomColComponent} from '@common/experiments/dumb/select-hyper-params-for-custom-col/select-hyper-params-for-custom-col.component';
import {PushPipe} from '@ngrx/component';
import {MatIcon} from '@angular/material/icon';
import {MatButton, MatIconButton} from '@angular/material/button';
import {MatDialogActions, MatDialogClose} from '@angular/material/dialog';
import {IsRowSelectedPipe} from '@common/shared/ui-components/data/table/is-rwo-selected.pipe';
import {MiniTagsListComponent} from '@common/shared/ui-components/tags/user-tag/mini-tags-list/mini-tags-list.component';
import {TableCardFilterComponent} from '@common/shared/ui-components/data/table/table-card-filter-template/table-card-filter.component';
export const experimentSyncedKeys = [
'view.projectColumnsSortOrder',
@ -126,7 +127,6 @@ export const getExperimentsConfig = (userPreferences: UserPreferences) => ({
const DECLARATIONS = [
ExperimentMenuComponent,
ExperimentMenuExtendedComponent,
ExperimentSystemTagsComponent,
ChangeProjectDialogComponent,
CloneDialogComponent,
AbortAllChildrenDialogComponent,
@ -151,9 +151,7 @@ const DECLARATIONS = [
ExperimentGraphsModule,
MatProgressSpinnerModule,
ScrollingModule,
CommonLayoutModule,
HyperParamMetricColumnComponent,
LabeledFormFieldDirective,
StringIncludedInArrayPipe,
TimeAgoPipe,
ReplaceViaMapPipe,
@ -163,7 +161,6 @@ const DECLARATIONS = [
UniqueNameValidatorDirective,
TagsMenuComponent,
CustomColumnsListComponent,
CheckboxControlComponent,
MenuComponent,
StatusIconLabelComponent,
ExperimentTypeIconLabelComponent,
@ -176,9 +173,8 @@ const DECLARATIONS = [
TagComponent,
MatMenuModule,
MatSidenavModule,
TableFilterSortTemplateComponent,
TableFilterSortComponent,
TableComponent,
TableCardFilterTemplateComponent,
MatSelectModule,
ButtonToggleComponent,
GroupedCheckedFilterListComponent,
@ -198,6 +194,15 @@ const DECLARATIONS = [
SelectMetricForCustomColComponent,
SelectHyperParamsForCustomColComponent,
PushPipe,
MatIcon,
MatIconButton,
MatDialogActions,
MatDialogClose,
MatButton,
IsRowSelectedPipe,
MiniTagsListComponent,
TableCardFilterComponent,
ReactiveFormsModule,
],
declarations : [...DECLARATIONS],
providers : [

View File

@ -40,7 +40,9 @@ export enum ExperimentTagsEnum {
Development = 'development',
Hidden = 'archived',
Shared = 'shared',
Pipeline = 'pipeline'
Pipeline = 'pipeline',
Example = 'example',
Dataset = 'dataset'
}
export const EXPERIMENTS_TAGS = {

View File

View File

@ -1,6 +1,7 @@
import {Model} from '../../../business-logic/model/models/model';
import {ISelectedExperiment} from './experiment-info.model';
import {isExample} from '../../../webapp-common/shared/utils/shared-utils';
import {ExperimentTagsEnum} from '~/features/experiments/shared/experiments.const';
export function areLabelsEqualss(modelLabels: Model['labels'], labels: Model['labels']) {
@ -12,11 +13,14 @@ export function isDevelopment(entity): boolean {
}
export function getSystemTags(experiment: ISelectedExperiment) {
const sysTags = [];
if (isExample(experiment)) {
sysTags.push('example');
const ignoredTags: string[] = [ExperimentTagsEnum.Hidden, ExperimentTagsEnum.Development];
ignoredTags.push(ExperimentTagsEnum.Pipeline, ExperimentTagsEnum.Dataset);
if (experiment?.system_tags?.includes(ExperimentTagsEnum.Dataset) || experiment?.system_tags?.includes(ExperimentTagsEnum.Pipeline)) {
ignoredTags.push(ExperimentTagsEnum.Development);
}
return sysTags;
return experiment?.system_tags?.filter(tag => !ignoredTags.includes(tag)) // remove ignored tags
.map(tag => tag === ExperimentTagsEnum.Development ? 'dev' : tag) // development => dev
.concat(isExample(experiment) ? [ExperimentTagsEnum.Example] : []); // add example when needed
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars

0
src/app/features/login/login.actions.ts Executable file → Normal file
View File

View File

@ -1,41 +1,10 @@
import { HttpClientModule } from '@angular/common/http';
import { FormsModule } from '@angular/forms';
import { NgModule } from '@angular/core';
import { CommonModule } from '@angular/common';
import { LoginRoutingModule } from './login-routing.module';
import { MatAutocompleteModule } from '@angular/material/autocomplete';
import {MatProgressSpinnerModule} from '@angular/material/progress-spinner';
import {MatCheckboxModule} from '@angular/material/checkbox';
import {SignupComponent} from './signup/signup.component';
import {MatFormFieldModule} from '@angular/material/form-field';
import {MatSelectModule} from '@angular/material/select';
import {MatInputModule} from '@angular/material/input';
import {MatRadioModule} from '@angular/material/radio';
import {LoginComponent} from '@common/login/login/login.component';
import {NtkmeButtonModule} from '@ctrl/ngx-github-buttons';
import {SafePipe} from '@common/shared/pipes/safe.pipe';
import {PushPipe} from '@ngrx/component';
@NgModule({
declarations: [LoginComponent, SignupComponent],
providers: [],
imports: [
CommonModule,
LoginRoutingModule,
FormsModule,
MatAutocompleteModule,
HttpClientModule,
MatProgressSpinnerModule,
MatCheckboxModule,
MatFormFieldModule,
MatSelectModule,
MatInputModule,
MatRadioModule,
NtkmeButtonModule,
SafePipe,
PushPipe
]
})
export class LoginModule { }

View File

@ -1,5 +1,5 @@
import { Component} from '@angular/core';
import {ModelMenuComponent} from '@common/models/containers/model-menu/model-menu.component';
import {Component, signal} from '@angular/core';
import {ModelMenuComponent} from '../../../../webapp-common/models/containers/model-menu/model-menu.component';
@Component({
selector: 'sm-model-menu-extended',
@ -7,7 +7,6 @@ import {ModelMenuComponent} from '@common/models/containers/model-menu/model-men
styleUrls: ['../../../../webapp-common/models/containers/model-menu/model-menu.component.scss']
})
export class ModelMenuExtendedComponent extends ModelMenuComponent {
get contextMenu() {
return this;
}
contextMenu = signal(this);
}

View File

@ -1,21 +1,23 @@
import {NgModule} from '@angular/core';
import {CommonModule} from '@angular/common';
import { ModelMenuComponent } from '@common/models/containers/model-menu/model-menu.component';
import {CommonLayoutModule} from '@common/layout/layout.module';
import { ModelMenuExtendedComponent } from './containers/model-menu-extended/model-menu-extended.component';
import {MenuItemTextPipe} from '@common/shared/pipes/menu-item-text.pipe';
import {TagsMenuComponent} from '@common/shared/ui-components/tags/tags-menu/tags-menu.component';
import {MatMenuModule} from '@angular/material/menu';
import {MatIcon} from '@angular/material/icon';
import {MatIconButton} from '@angular/material/button';
@NgModule({
declarations: [ModelMenuComponent, ModelMenuExtendedComponent],
exports : [ModelMenuComponent, ModelMenuExtendedComponent],
imports : [
CommonLayoutModule,
imports: [
CommonModule,
MenuItemTextPipe,
TagsMenuComponent,
MatMenuModule
MatMenuModule,
MatIcon,
MatIconButton
]
})
export class FeatureModelsModule {

0
src/app/features/not-found/not-found-routing.module.ts Executable file → Normal file
View File

12
src/app/features/not-found/not-found.module.ts Executable file → Normal file
View File

@ -4,13 +4,15 @@ import { CommonModule } from '@angular/common';
import { NotFoundRoutingModule } from './not-found-routing.module';
import { NotFoundComponent } from './not-found/not-found.component';
import {CheckPermissionDirective} from '~/shared/directives/check-permission.directive';
import {MatIcon} from '@angular/material/icon';
@NgModule({
imports: [
CommonModule,
NotFoundRoutingModule,
CheckPermissionDirective
],
imports: [
CommonModule,
NotFoundRoutingModule,
CheckPermissionDirective,
MatIcon
],
declarations: [NotFoundComponent]
})
export class NotFoundModule { }

View File

@ -1,6 +1,6 @@
<div id="not-found-container" class="">
<div class="d-flex justify-content-center">
<div class="cat al-icon al-ico-cat al-color white"></div>
<div class="d-flex justify-content-center align-items-center">
<mat-icon fontSet="al" fontIcon="al-ico-cat" class="cat"></mat-icon>
<h1>404</h1>
</div>

View File

@ -1,13 +1,14 @@
@import "../../../webapp-common/shared/ui-components/styles/variables";
@import "variables";
:host{
#not-found-container {
margin-top: 5%;
color: white;
color: var(--color-on-surface);
h1 {
font-size: 200px;
font-weight: bold;
margin: 0;
}
h2 {
@ -19,38 +20,10 @@
font-weight: 100;
}
}
.navbar-links {
li {
list-style: none;
height: 130px;
width: 130px;
border: 1px solid white;
border-radius: 4px;
margin-left: 15px;
margin-right: 15px;
}
li:hover {
background: $blue-600;
}
.icon {
margin: 10px auto;
width: 55px;
height: 55px;
display: block;
}
span {
text-align: center;
font-size: 17px;
font-weight: 400;
display: block;
color: white;
}
}
.cat{
.cat {
font-size: 140px;
width:140px;
height:140px;
margin-top:44px;
width: 140px;
height: 140px;
}
}

View File

4
src/app/features/projects/projects-page.utils.ts Executable file → Normal file
View File

@ -10,7 +10,7 @@ import {ProjectsGetAllExRequest} from '~/business-logic/model/projects/projectsG
export const isDeletableProject = readyForDeletion => (readyForDeletion.experiments.unarchived + readyForDeletion.models.unarchived) === 0;
export const popupEntitiesListConst = 'experiments, dataviews pipelines or dataset';
export const popupEntitiesListConst = 'tasks, pipelines or dataset';
export const getDeleteProjectPopupStatsBreakdown = (readyForDeletion, statsSubset: 'archived' | 'unarchived' | 'total', experimentCaption) => {
const errors = [
@ -31,7 +31,7 @@ export const readyForDeletionFilter = readyForDeletion => !(readyForDeletion.exp
export const isDatasets = (snapshot: ActivatedRouteSnapshot) => snapshot.firstChild.routeConfig.path === 'datasets';
export const routeConfToProjectType = (routeConf: string[]) => routeConf[0];
export const getNoProjectsReRoute = ((routeConf: string[]) => 'experiments');
export const getNoProjectsReRoute = (() => 'tasks');
export const isNestedDatasets = (routeConf: string[]) => ['simple'].includes(routeConf?.[1]);
export const getFeatureProjectRequest = (snapshot: ActivatedRouteSnapshot, nested: boolean, searchQuery: any, selectedProjectName: any, selectedProjectId: any): ProjectsGetAllExRequest => {

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