This is an automated email from the ASF dual-hosted git repository.

dominikriemer pushed a commit to branch dev
in repository https://gitbox.apache.org/repos/asf/streampipes.git


The following commit(s) were added to refs/heads/dev by this push:
     new c0127c93d7 feat: Improve home screen loading behaviour (#4555)
c0127c93d7 is described below

commit c0127c93d764d148da4b15acec29cb8e10a14d82
Author: Dominik Riemer <[email protected]>
AuthorDate: Sat Jun 13 14:06:46 2026 +0200

    feat: Improve home screen loading behaviour (#4555)
---
 ui/deployment/modules.yml                          |   8 +-
 .../registration-allowed.can-activate.guard.ts     |   6 +-
 .../restore-password-allowed.can-activate.guard.ts |   8 +-
 .../guards/terms.can-activate-children.guard.ts    |   6 +-
 ui/src/app/help/help.component.ts                  |   6 +-
 ui/src/app/home/components/status.component.ts     |   2 +-
 .../welcome-tour/welcome-tour.component.html       |  45 +++++--
 .../welcome-tour/welcome-tour.component.scss       |  10 ++
 .../dialog/welcome-tour/welcome-tour.component.ts  | 127 +++++++++++++++++-
 ui/src/app/home/home.component.ts                  | 148 +++------------------
 .../login/components/base-login-page.directive.ts  |   4 +-
 .../services/login-settings.service.ts}            |  50 ++++---
 ui/src/app/login/services/login.service.ts         |  16 +--
 13 files changed, 233 insertions(+), 203 deletions(-)

diff --git a/ui/deployment/modules.yml b/ui/deployment/modules.yml
index f8a7b05de1..429c018463 100644
--- a/ui/deployment/modules.yml
+++ b/ui/deployment/modules.yml
@@ -59,7 +59,7 @@ spConnect:
         createLinks: "['connect', 'catalog']"
         title: 'Adapters'
         createTitle: 'New adapter'
-        dataFns: '[this.adapterService.getAdapters()]'
+        dataFns: '[this.adapterService.getAdapterSummary()]'
         viewRoles: '[UserPrivilege.PRIVILEGE_READ_ADAPTER, 
UserPrivilege.PRIVILEGE_WRITE_ADAPTER]'
         createRoles: '[UserPrivilege.PRIVILEGE_WRITE_ADAPTER]'
     category: 'management'
@@ -78,7 +78,7 @@ spPipelines:
         createLinks: "['pipelines', 'create']"
         title: 'Pipelines'
         createTitle: 'New pipeline'
-        dataFns: '[this.pipelineService.getPipelines()]'
+        dataFns: '[this.pipelineService.getPipelineSummary()]'
         viewRoles: '[UserPrivilege.PRIVILEGE_READ_PIPELINE, 
UserPrivilege.PRIVILEGE_WRITE_PIPELINE]'
         createRoles: '[UserPrivilege.PRIVILEGE_WRITE_PIPELINE]'
     category: 'analytics'
@@ -107,7 +107,7 @@ spDashboard:
         createLinks: "['dashboard']"
         title: 'Dashboards'
         createTitle: 'New dashboard'
-        dataFns: '[this.dashboardService.getDashboards()]'
+        dataFns: '[this.dashboardService.getDashboardSummary()]'
         viewRoles: '[UserPrivilege.PRIVILEGE_READ_DASHBOARD, 
UserPrivilege.PRIVILEGE_WRITE_DASHBOARD]'
         createRoles: '[UserPrivilege.PRIVILEGE_WRITE_DASHBOARD]'
     category: 'visualization'
@@ -127,7 +127,7 @@ spChart:
         createLinks: "['chart']"
         title: 'Charts'
         createTitle: 'New chart'
-        dataFns: '[this.chartService.getAllCharts()]'
+        dataFns: '[this.chartService.getChartSummary()]'
         viewRoles: '[UserPrivilege.PRIVILEGE_READ_DATA_EXPLORER_VIEW, 
UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW]'
         createRoles: '[UserPrivilege.PRIVILEGE_WRITE_DATA_EXPLORER_VIEW]'
     category: 'visualization'
diff --git 
a/ui/src/app/core/setup/guards/registration-allowed.can-activate.guard.ts 
b/ui/src/app/core/setup/guards/registration-allowed.can-activate.guard.ts
index ccccc288e5..2b7f1e86ae 100644
--- a/ui/src/app/core/setup/guards/registration-allowed.can-activate.guard.ts
+++ b/ui/src/app/core/setup/guards/registration-allowed.can-activate.guard.ts
@@ -23,14 +23,14 @@ import {
     RouterStateSnapshot,
     UrlTree,
 } from '@angular/router';
-import { LoginService } from '../../../login/services/login.service';
+import { LoginSettingsService } from 
'../../../login/services/login-settings.service';
 import { Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
 
 @Injectable({ providedIn: 'root' })
 export class RegistrationAllowedCanActivateGuard {
     private router = inject(Router);
-    private loginService = inject(LoginService);
+    private loginSettingsService = inject(LoginSettingsService);
 
     canActivate(
         _route: ActivatedRouteSnapshot,
@@ -40,7 +40,7 @@ export class RegistrationAllowedCanActivateGuard {
         | Promise<boolean | UrlTree>
         | boolean
         | UrlTree {
-        return this.loginService.fetchLoginSettings().pipe(
+        return this.loginSettingsService.getSettings().pipe(
             map(config => {
                 return config.allowSelfRegistration
                     ? true
diff --git 
a/ui/src/app/core/setup/guards/restore-password-allowed.can-activate.guard.ts 
b/ui/src/app/core/setup/guards/restore-password-allowed.can-activate.guard.ts
index e7f51df072..66c1060aef 100644
--- 
a/ui/src/app/core/setup/guards/restore-password-allowed.can-activate.guard.ts
+++ 
b/ui/src/app/core/setup/guards/restore-password-allowed.can-activate.guard.ts
@@ -23,14 +23,14 @@ import {
     RouterStateSnapshot,
     UrlTree,
 } from '@angular/router';
-import { LoginService } from '../../../login/services/login.service';
+import { LoginSettingsService } from 
'../../../login/services/login-settings.service';
 import { Observable } from 'rxjs';
 import { map } from 'rxjs/operators';
 
 @Injectable({ providedIn: 'root' })
 export class RestorePasswordAllowedCanActivateGuard {
     private router = inject(Router);
-    private loginService = inject(LoginService);
+    private loginSettingsService = inject(LoginSettingsService);
 
     canActivate(
         _route: ActivatedRouteSnapshot,
@@ -40,8 +40,8 @@ export class RestorePasswordAllowedCanActivateGuard {
         | Promise<boolean | UrlTree>
         | boolean
         | UrlTree {
-        return this.loginService
-            .fetchLoginSettings()
+        return this.loginSettingsService
+            .getSettings()
             .pipe(
                 map(config =>
                     config.allowPasswordRecovery
diff --git a/ui/src/app/core/setup/guards/terms.can-activate-children.guard.ts 
b/ui/src/app/core/setup/guards/terms.can-activate-children.guard.ts
index 7eaf577d50..deb2e4e2f0 100644
--- a/ui/src/app/core/setup/guards/terms.can-activate-children.guard.ts
+++ b/ui/src/app/core/setup/guards/terms.can-activate-children.guard.ts
@@ -26,7 +26,7 @@ import {
     RouterStateSnapshot,
 } from '@angular/router';
 import { CurrentUserService } from '@streampipes/shared-ui';
-import { LoginService } from '../../../login/services/login.service';
+import { LoginSettingsService } from 
'../../../login/services/login-settings.service';
 import { of, take } from 'rxjs';
 import { catchError, map } from 'rxjs/operators';
 
@@ -37,7 +37,7 @@ export class TermsCanActivateChildrenGuard implements 
CanActivateChild {
         state: RouterStateSnapshot,
     ): MaybeAsync<GuardResult> {
         const currentUser = this.currentUserService.getCurrentUser();
-        return this.loginService.fetchLoginSettings().pipe(
+        return this.loginSettingsService.getSettings().pipe(
             take(1),
             map(settings => {
                 const needsAck =
@@ -56,7 +56,7 @@ export class TermsCanActivateChildrenGuard implements 
CanActivateChild {
     }
 
     private currentUserService = inject(CurrentUserService);
-    private loginService = inject(LoginService);
+    private loginSettingsService = inject(LoginSettingsService);
 
     private router = inject(Router);
 }
diff --git a/ui/src/app/help/help.component.ts 
b/ui/src/app/help/help.component.ts
index f85d009b64..214e696ad6 100644
--- a/ui/src/app/help/help.component.ts
+++ b/ui/src/app/help/help.component.ts
@@ -27,7 +27,7 @@ import {
     LayoutDirective,
 } from '@ngbracket/ngx-layout/flex';
 import { MatTab, MatTabGroup } from '@angular/material/tabs';
-import { LoginService } from '../login/services/login.service';
+import { LoginSettingsService } from 
'../login/services/login-settings.service';
 import { InfoTabComponent } from './components/info/info.component';
 import { DocumentationTabComponent } from 
'./components/documentation/documentation.component';
 import { ShortcutsTabComponent } from 
'./components/shortcuts/shortcuts.component';
@@ -52,7 +52,7 @@ import { TranslatePipe, TranslateService } from 
'@ngx-translate/core';
 })
 export class HelpComponent implements OnInit {
     private breadcrumbService = inject(SpBreadcrumbService);
-    private loginService = inject(LoginService);
+    private loginSettingsService = inject(LoginSettingsService);
     private translateService = inject(TranslateService);
 
     selectedIndex = 0;
@@ -67,7 +67,7 @@ export class HelpComponent implements OnInit {
         this.breadcrumbService.updateBreadcrumb([
             { label: this.translateService.instant('Help') },
         ]);
-        this.loginService.fetchLoginSettings().subscribe(res => {
+        this.loginSettingsService.getSettings().subscribe(res => {
             this.documentationLink = res.linkSettings?.documentationUrl || '';
             this.showDocumentationTab =
                 !!res.linkSettings?.showDocumentationLinkInProfileMenu &&
diff --git a/ui/src/app/home/components/status.component.ts 
b/ui/src/app/home/components/status.component.ts
index 58872ec0cf..5143929bd0 100644
--- a/ui/src/app/home/components/status.component.ts
+++ b/ui/src/app/home/components/status.component.ts
@@ -55,7 +55,7 @@ export class StatusComponent implements OnInit {
         zip(this.statusBox.dataFns).subscribe(res => {
             let totalLength = 0;
             res.forEach(response => {
-                totalLength += response.length;
+                totalLength += response.totalCount;
             });
 
             this.resourceCount = totalLength;
diff --git a/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.html 
b/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.html
index 150983d1a9..90c028bd2c 100644
--- a/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.html
+++ b/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.html
@@ -24,12 +24,6 @@
                 ><h3>Thank you for using {{ appConstants.APP_NAME }}.</h3></b
             >
         </div>
-        <div>
-            <img
-                src="assets/img/sp-pipeline-white-small.png"
-                style="margin-left: auto; margin-right: auto; display: block"
-            />
-        </div>
         <div fxFlex="100" fxLayoutAlign="center center" fxLayout="column">
             <div fxFlex="100" fxLayoutAlign="center center" fxLayout="column">
                 <b
@@ -40,15 +34,38 @@
                     If you are using {{ appConstants.APP_NAME }} for the first
                     time, we recommend taking the interactive tour!
                 </p>
+                @if (loadingTutorialResources) {
+                    <div
+                        class="tutorial-status"
+                        fxLayout="row"
+                        fxLayoutAlign="center center"
+                        fxLayoutGap="10px"
+                    >
+                        <mat-spinner [diameter]="20"></mat-spinner>
+                        <span>Checking tutorial pipeline elements...</span>
+                    </div>
+                }
+                @if (showMissingElementsHint) {
+                    <sp-alert-banner
+                        type="info"
+                        title="Missing Elements for Tutorial"
+                        description="The tutorial needs additional pipeline 
elements.
+                      Install the missing extensions (Settings -> Extensions) 
and reopen the tour."
+                    >
+                    </sp-alert-banner>
+                }
             </div>
-            <button
-                mat-button
-                mat-flat-button
-                color="accent"
-                (click)="close(true)"
-            >
-                Start Tour
-            </button>
+            @if (!loadingTutorialResources && !showMissingElementsHint) {
+                <button
+                    mat-button
+                    mat-flat-button
+                    color="accent"
+                    [disabled]="loadingTutorialResources"
+                    (click)="startTutorial()"
+                >
+                    Start Tour
+                </button>
+            }
         </div>
     </div>
     <mat-divider></mat-divider>
diff --git a/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.scss 
b/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.scss
index c6405f3d58..a06a4f52eb 100644
--- a/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.scss
+++ b/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.scss
@@ -38,3 +38,13 @@
         border-color: transparent;
     }
 }
+
+.tutorial-status {
+    margin-top: 0.5rem;
+}
+
+.tutorial-hint {
+    margin: 0.5rem 0 0;
+    text-align: center;
+    max-width: 28rem;
+}
diff --git a/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.ts 
b/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.ts
index 60fe452a98..8e57d2e7f4 100644
--- a/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.ts
+++ b/ui/src/app/home/dialog/welcome-tour/welcome-tour.component.ts
@@ -16,11 +16,23 @@
  *
  */
 
-import { DialogRef } from '@streampipes/shared-ui';
+import {
+    DialogRef,
+    DialogService,
+    PanelType,
+    SpAlertBannerComponent,
+} from '@streampipes/shared-ui';
 import { Component, Input, OnInit, inject } from '@angular/core';
 import { AppConstants } from '../../../services/app.constants';
 import { AuthService } from '../../../services/auth.service';
-import { UserAccount, UserInfo } from '@streampipes/platform-services';
+import {
+    AdapterDescription,
+    AdapterService,
+    NamedStreamPipesEntity,
+    PipelineElementService,
+    UserAccount,
+    UserInfo,
+} from '@streampipes/platform-services';
 import { ProfileService } from '../../../profile/profile.service';
 import {
     FlexDirective,
@@ -29,6 +41,10 @@ import {
 } from '@ngbracket/ngx-layout/flex';
 import { MatButton } from '@angular/material/button';
 import { MatDivider } from '@angular/material/divider';
+import { MatProgressSpinner } from '@angular/material/progress-spinner';
+import { MissingElementsForTutorialComponent } from 
'../../../editor/dialog/missing-elements-for-tutorial/missing-elements-for-tutorial.component';
+import { forkJoin } from 'rxjs';
+import { LayoutGapDirective } from '@ngbracket/ngx-layout';
 
 @Component({
     selector: 'sp-welcome-tour',
@@ -40,18 +56,36 @@ import { MatDivider } from '@angular/material/divider';
         LayoutAlignDirective,
         MatButton,
         MatDivider,
+        MatProgressSpinner,
+        LayoutGapDirective,
+        SpAlertBannerComponent,
     ],
 })
 export class WelcomeTourComponent implements OnInit {
+    private static readonly REQUIRED_ADAPTER_FOR_TUTORIAL_APP_ID =
+        'org.apache.streampipes.connect.iiot.adapters.simulator.machine';
+    private static readonly REQUIRED_PROCESSOR_FOR_TUTORIAL_APP_ID =
+        'org.apache.streampipes.processors.filters.jvm.numericalfilter';
+    private static readonly REQUIRED_SINK_FOR_TUTORIAL_APP_ID =
+        'org.apache.streampipes.sinks.internal.jvm.datalake';
+
     private authService = inject(AuthService);
     private dialogRef = inject<DialogRef<WelcomeTourComponent>>(DialogRef);
     private profileService = inject(ProfileService);
+    private dialogService = inject(DialogService);
+    private adapterService = inject(AdapterService);
+    private pipelineElementService = inject(PipelineElementService);
     appConstants = inject(AppConstants);
 
     @Input()
     userInfo: UserInfo;
 
     currentUser: UserAccount;
+    availablePipelineElements: NamedStreamPipesEntity[] = [];
+    availableAdapters: AdapterDescription[] = [];
+    missingElementsForTutorial = [];
+    tutorialAvailable = false;
+    loadingTutorialResources = true;
 
     ngOnInit(): void {
         this.profileService
@@ -59,6 +93,24 @@ export class WelcomeTourComponent implements OnInit {
             .subscribe(data => {
                 this.currentUser = data;
             });
+
+        forkJoin([
+            this.adapterService.getAdapterDescriptions(),
+            this.pipelineElementService.getDataStreams(),
+            this.pipelineElementService.getDataProcessors(),
+            this.pipelineElementService.getDataSinks(),
+        ]).subscribe(res => {
+            this.availableAdapters = res[0];
+            this.availablePipelineElements = []
+                .concat(...res[1])
+                .concat(...res[2])
+                .concat(...res[3]);
+            this.missingElementsForTutorial =
+                this.getMissingElementsForTutorial();
+            this.tutorialAvailable =
+                this.missingElementsForTutorial.length === 0;
+            this.loadingTutorialResources = false;
+        });
     }
 
     hideTourForever() {
@@ -71,7 +123,78 @@ export class WelcomeTourComponent implements OnInit {
             });
     }
 
+    startTutorial() {
+        if (this.loadingTutorialResources) {
+            return;
+        }
+
+        if (this.tutorialAvailable) {
+            this.close(true);
+        } else {
+            this.dialogService.open(MissingElementsForTutorialComponent, {
+                panelType: PanelType.STANDARD_PANEL,
+                title: 'Tutorial requires pipeline elements',
+                data: {
+                    missingElementsForTutorial: 
this.missingElementsForTutorial,
+                },
+            });
+        }
+    }
+
     close(startTutorial = false) {
         this.dialogRef.close(startTutorial);
     }
+
+    get showMissingElementsHint() {
+        return !this.loadingTutorialResources && !this.tutorialAvailable;
+    }
+
+    private getMissingElementsForTutorial() {
+        const missingElements = [];
+
+        if (
+            !this.requiredPeForTourPresent(
+                this.availableAdapters,
+                WelcomeTourComponent.REQUIRED_ADAPTER_FOR_TUTORIAL_APP_ID,
+            )
+        ) {
+            missingElements.push({
+                name: 'Machine Data Simulator',
+                appId: 
WelcomeTourComponent.REQUIRED_ADAPTER_FOR_TUTORIAL_APP_ID,
+            });
+        }
+
+        if (
+            !this.requiredPeForTourPresent(
+                this.availablePipelineElements,
+                WelcomeTourComponent.REQUIRED_PROCESSOR_FOR_TUTORIAL_APP_ID,
+            )
+        ) {
+            missingElements.push({
+                name: 'Numerical Filter',
+                appId: 
WelcomeTourComponent.REQUIRED_PROCESSOR_FOR_TUTORIAL_APP_ID,
+            });
+        }
+
+        if (
+            !this.requiredPeForTourPresent(
+                this.availablePipelineElements,
+                WelcomeTourComponent.REQUIRED_SINK_FOR_TUTORIAL_APP_ID,
+            )
+        ) {
+            missingElements.push({
+                name: 'Dashboard Sink',
+                appId: WelcomeTourComponent.REQUIRED_SINK_FOR_TUTORIAL_APP_ID,
+            });
+        }
+
+        return missingElements;
+    }
+
+    private requiredPeForTourPresent(
+        list: Array<{ appId: string }>,
+        appId: string,
+    ) {
+        return list?.some(el => el.appId === appId);
+    }
 }
diff --git a/ui/src/app/home/home.component.ts 
b/ui/src/app/home/home.component.ts
index 5a625422db..a73f4303aa 100644
--- a/ui/src/app/home/home.component.ts
+++ b/ui/src/app/home/home.component.ts
@@ -31,12 +31,9 @@ import {
     SplitSectionComponent,
 } from '@streampipes/shared-ui';
 import { UserRole } from '../core/auth/user-role.enum';
-import { MissingElementsForTutorialComponent } from 
'../editor/dialog/missing-elements-for-tutorial/missing-elements-for-tutorial.component';
 import { WelcomeTourComponent } from 
'./dialog/welcome-tour/welcome-tour.component';
 import { ShepherdService } from '../services/tour/shepherd.service';
 import {
-    AdapterDescription,
-    AdapterService,
     AssetConstants,
     AssetLinkType,
     AssetManagementService,
@@ -44,12 +41,10 @@ import {
     GenericStorageService,
     LocationConfig,
     LocationConfigService,
-    NamedStreamPipesEntity,
-    PipelineElementService,
     SpAssetModel,
     UserInfo,
 } from '@streampipes/platform-services';
-import { forkJoin, Subscription, zip } from 'rxjs';
+import { forkJoin, Subscription } from 'rxjs';
 import { StatusBox } from './models/home.model';
 import {
     FlexDirective,
@@ -92,9 +87,6 @@ export class HomeComponent implements OnInit, OnDestroy {
     serviceLinks = [];
     showStatus = false;
 
-    availablePipelineElements: NamedStreamPipesEntity[] = [];
-    availableAdapters: AdapterDescription[] = [];
-
     statusBoxes: StatusBox[] = [];
     locationConfig: LocationConfig;
     assets: SpAssetModel[] = [];
@@ -102,14 +94,6 @@ export class HomeComponent implements OnInit, OnDestroy {
     sites: Record<string, AssetSiteDesc> = {};
     assetLinkTypes: Record<string, AssetLinkType> = {};
 
-    requiredAdapterForTutorialAppId: any =
-        'org.apache.streampipes.connect.iiot.adapters.simulator.machine';
-    requiredProcessorForTutorialAppId: any =
-        'org.apache.streampipes.processors.filters.jvm.numericalfilter';
-    requiredSinkForTutorialAppId: any =
-        'org.apache.streampipes.sinks.internal.jvm.datalake';
-    missingElementsForTutorial: any = [];
-
     isTutorialOpen = false;
     currentUser: UserInfo;
     selectedView = signal<string>('table');
@@ -122,8 +106,6 @@ export class HomeComponent implements OnInit, OnDestroy {
     private breadcrumbService = inject(SpBreadcrumbService);
     private dialogService = inject(DialogService);
     private shepherdService = inject(ShepherdService);
-    private pipelineElementService = inject(PipelineElementService);
-    private adapterService = inject(AdapterService);
     private genericStorageService = inject(GenericStorageService);
     private locationService = inject(LocationConfigService);
     private assetService = inject(AssetManagementService);
@@ -175,8 +157,10 @@ export class HomeComponent implements OnInit, OnDestroy {
             this.contentLoaded = true;
             this.showStatus = true;
         });
-        if (isAdmin) {
-            this.loadResources();
+        console.log(isAdmin);
+        console.log(this.currentUser.showTutorial);
+        if (isAdmin && this.currentUser.showTutorial) {
+            this.checkForTutorial();
         }
         this.breadcrumbService.updateBreadcrumb([]);
     }
@@ -199,115 +183,27 @@ export class HomeComponent implements OnInit, OnDestroy {
     }
 
     checkForTutorial() {
-        if (this.currentUser.showTutorial) {
-            if (this.requiredPipelineElementsForTourPresent()) {
-                this.isTutorialOpen = true;
-                const dialogRef = this.dialogService.open(
-                    WelcomeTourComponent,
-                    {
-                        panelType: PanelType.STANDARD_PANEL,
-                        title: 'Welcome to ' + this.appConstants.APP_NAME,
-                        data: {
-                            userInfo: this.currentUser,
-                        },
-                    },
-                );
-                dialogRef.afterClosed().subscribe(startTutorial => {
-                    if (startTutorial) {
-                        this.startTutorial();
-                    }
-                });
+        this.isTutorialOpen = true;
+        const dialogRef = this.dialogService.open(WelcomeTourComponent, {
+            panelType: PanelType.STANDARD_PANEL,
+            title: 'Welcome to ' + this.appConstants.APP_NAME,
+            data: {
+                userInfo: this.currentUser,
+            },
+        });
+        dialogRef.afterClosed().subscribe(startTutorial => {
+            if (startTutorial) {
+                this.startTutorial();
+            } else {
+                this.isTutorialOpen = false;
             }
-        }
+        });
     }
 
     startTutorial() {
-        if (this.requiredPipelineElementsForTourPresent()) {
-            this.router.navigate(['connect']).then(() => {
-                this.shepherdService.startAdapterTour();
-            });
-        } else {
-            this.missingElementsForTutorial = [];
-            if (!this.requiredAdapterForTutorialAppId()) {
-                this.missingElementsForTutorial.push({
-                    name: 'Machine Data Simulator',
-                    appId: this.requiredAdapterForTutorialAppId,
-                });
-            }
-            if (!this.requiredProcessorForTourPresent()) {
-                this.missingElementsForTutorial.push({
-                    name: 'Numerical Filter',
-                    appId: this.requiredProcessorForTutorialAppId,
-                });
-            }
-            if (!this.requiredSinkForTourPresent()) {
-                this.missingElementsForTutorial.push({
-                    name: 'Dashboard Sink',
-                    appId: this.requiredSinkForTutorialAppId,
-                });
-            }
-
-            this.dialogService.open(MissingElementsForTutorialComponent, {
-                panelType: PanelType.STANDARD_PANEL,
-                title: 'Tutorial requires pipeline elements',
-                data: {
-                    missingElementsForTutorial: 
this.missingElementsForTutorial,
-                },
-            });
-        }
-    }
-
-    requiredPipelineElementsForTourPresent() {
-        return (
-            this.requiredAdapterForTourPresent() &&
-            this.requiredProcessorForTourPresent() &&
-            this.requiredSinkForTourPresent()
-        );
-    }
-
-    requiredAdapterForTourPresent() {
-        return this.requiredPeForTourPresent(
-            this.availableAdapters,
-            this.requiredAdapterForTutorialAppId,
-        );
-    }
-
-    requiredProcessorForTourPresent() {
-        return this.requiredPeForTourPresent(
-            this.availablePipelineElements,
-            this.requiredProcessorForTutorialAppId,
-        );
-    }
-
-    requiredSinkForTourPresent() {
-        return this.requiredPeForTourPresent(
-            this.availablePipelineElements,
-            this.requiredSinkForTutorialAppId,
-        );
-    }
-
-    requiredPeForTourPresent(list: NamedStreamPipesEntity[], appId: string) {
-        return (
-            list &&
-            list.some(el => {
-                return el.appId === appId;
-            })
-        );
-    }
-
-    loadResources(): void {
-        zip(
-            this.adapterService.getAdapterDescriptions(),
-            this.pipelineElementService.getDataStreams(),
-            this.pipelineElementService.getDataProcessors(),
-            this.pipelineElementService.getDataSinks(),
-        ).subscribe(res => {
-            this.availableAdapters = res[0];
-            this.availablePipelineElements = this.availablePipelineElements
-                .concat(...res[1])
-                .concat(...res[2])
-                .concat(...res[3]);
-            this.checkForTutorial();
+        this.router.navigate(['connect']).then(() => {
+            this.shepherdService.startAdapterTour();
+            this.isTutorialOpen = false;
         });
     }
 
diff --git a/ui/src/app/login/components/base-login-page.directive.ts 
b/ui/src/app/login/components/base-login-page.directive.ts
index ef15cb926f..87228dbc98 100644
--- a/ui/src/app/login/components/base-login-page.directive.ts
+++ b/ui/src/app/login/components/base-login-page.directive.ts
@@ -18,6 +18,7 @@
 
 import { Directive, inject, OnInit } from '@angular/core';
 import { LoginService } from '../services/login.service';
+import { LoginSettingsService } from '../services/login-settings.service';
 import { LoginModel } from './login/login.model';
 
 @Directive()
@@ -26,9 +27,10 @@ export abstract class BaseLoginPageDirective implements 
OnInit {
     protected configReady = false;
 
     protected loginService = inject(LoginService);
+    protected loginSettingsService = inject(LoginSettingsService);
 
     ngOnInit(): void {
-        this.loginService.fetchLoginSettings().subscribe(result => {
+        this.loginSettingsService.getSettings().subscribe(result => {
             this.loginSettings = result;
             this.configReady = true;
             this.onSettingsAvailable();
diff --git 
a/ui/src/app/core/setup/guards/registration-allowed.can-activate.guard.ts 
b/ui/src/app/login/services/login-settings.service.ts
similarity index 51%
copy from 
ui/src/app/core/setup/guards/registration-allowed.can-activate.guard.ts
copy to ui/src/app/login/services/login-settings.service.ts
index ccccc288e5..347a74399b 100644
--- a/ui/src/app/core/setup/guards/registration-allowed.can-activate.guard.ts
+++ b/ui/src/app/login/services/login-settings.service.ts
@@ -17,35 +17,31 @@
  */
 
 import { Injectable, inject } from '@angular/core';
-import {
-    ActivatedRouteSnapshot,
-    Router,
-    RouterStateSnapshot,
-    UrlTree,
-} from '@angular/router';
-import { LoginService } from '../../../login/services/login.service';
-import { Observable } from 'rxjs';
-import { map } from 'rxjs/operators';
+import { HttpClient } from '@angular/common/http';
+import { PlatformServicesCommons } from '@streampipes/platform-services';
+import { Observable, shareReplay } from 'rxjs';
+import { LoginModel } from '../components/login/login.model';
 
 @Injectable({ providedIn: 'root' })
-export class RegistrationAllowedCanActivateGuard {
-    private router = inject(Router);
-    private loginService = inject(LoginService);
+export class LoginSettingsService {
+    private http = inject(HttpClient);
+    private platformServicesCommons = inject(PlatformServicesCommons);
 
-    canActivate(
-        _route: ActivatedRouteSnapshot,
-        _state: RouterStateSnapshot,
-    ):
-        | Observable<boolean | UrlTree>
-        | Promise<boolean | UrlTree>
-        | boolean
-        | UrlTree {
-        return this.loginService.fetchLoginSettings().pipe(
-            map(config => {
-                return config.allowSelfRegistration
-                    ? true
-                    : this.router.parseUrl('register');
-            }),
-        );
+    private settings$?: Observable<LoginModel>;
+
+    getSettings(): Observable<LoginModel> {
+        if (!this.settings$) {
+            this.settings$ = this.http
+                .get<LoginModel>(
+                    
`${this.platformServicesCommons.apiBasePath}/auth/settings`,
+                )
+                .pipe(shareReplay(1));
+        }
+
+        return this.settings$;
+    }
+
+    invalidateCache(): void {
+        this.settings$ = undefined;
     }
 }
diff --git a/ui/src/app/login/services/login.service.ts 
b/ui/src/app/login/services/login.service.ts
index d5b17f1df1..95a886acff 100644
--- a/ui/src/app/login/services/login.service.ts
+++ b/ui/src/app/login/services/login.service.ts
@@ -19,8 +19,7 @@
 import { Injectable, inject } from '@angular/core';
 import { HttpClient, HttpContext } from '@angular/common/http';
 import { PlatformServicesCommons } from '@streampipes/platform-services';
-import { Observable, shareReplay } from 'rxjs';
-import { LoginModel } from '../components/login/login.model';
+import { Observable } from 'rxjs';
 import { RegistrationModel } from '../components/register/registration.model';
 import { NGX_LOADING_BAR_IGNORED } from '@ngx-loading-bar/http-client';
 
@@ -29,19 +28,6 @@ export class LoginService {
     private http = inject(HttpClient);
     private platformServicesCommons = inject(PlatformServicesCommons);
 
-    private settings$?: Observable<LoginModel>;
-
-    fetchLoginSettings(): Observable<LoginModel> {
-        if (!this.settings$) {
-            this.settings$ = this.http
-                .get<LoginModel>(
-                    
`${this.platformServicesCommons.apiBasePath}/auth/settings`,
-                )
-                .pipe(shareReplay({ bufferSize: 1, refCount: true }));
-        }
-        return this.settings$;
-    }
-
     login(credentials): Observable<any> {
         return this.http.post(
             this.platformServicesCommons.apiBasePath + '/auth/login',

Reply via email to