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

riemer 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 19351a9aeb refactor: Improve registration of available settings views 
(#4234)
19351a9aeb is described below

commit 19351a9aeb96dfc4b77ee97d751c0b579bc8817c
Author: Dominik Riemer <[email protected]>
AuthorDate: Mon Mar 9 09:13:05 2026 +0100

    refactor: Improve registration of available settings views (#4234)
---
 .../api/system/ISpCoreConfigurationStorage.java    |   4 -
 .../impl/system/CoreConfigurationStorageImpl.java  |  10 +-
 .../lib/models/sp-configuration-section.model.ts   |  30 ++---
 .../lib/services/configuration-sections.service.ts | 104 +++++++++++++++++
 .../lib/services/configuration-sections.token.ts   |  34 +++---
 .../streampipes/shared-ui/src/public-api.ts        |   3 +
 ui/src/app/app.config.ts                           |   2 +
 .../app/configuration/configuration-route.guard.ts |  22 +++-
 .../configuration-section-host.component.ts        |  59 ++++++++++
 .../configuration-sections.providers.ts            | 129 +++++++++++++++++++++
 .../configuration/configuration-tabs.service.ts    | 100 ++++------------
 ui/src/app/configuration/configuration.routes.ts   |  68 ++---------
 12 files changed, 374 insertions(+), 191 deletions(-)

diff --git 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
index 468bbf5a05..38aafa7c41 100644
--- 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
+++ 
b/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
@@ -20,14 +20,10 @@ package org.apache.streampipes.storage.api.system;
 
 import org.apache.streampipes.model.configuration.SpCoreConfiguration;
 
-import java.util.List;
-
 public interface ISpCoreConfigurationStorage {
 
   boolean exists();
 
-  List<SpCoreConfiguration> getAll();
-
   void createElement(SpCoreConfiguration element);
 
   SpCoreConfiguration get();
diff --git 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/system/CoreConfigurationStorageImpl.java
 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/system/CoreConfigurationStorageImpl.java
index f18edc7394..19c28a454f 100644
--- 
a/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/system/CoreConfigurationStorageImpl.java
+++ 
b/streampipes-storage-couchdb/src/main/java/org/apache/streampipes/storage/couchdb/impl/system/CoreConfigurationStorageImpl.java
@@ -23,24 +23,16 @@ import 
org.apache.streampipes.storage.api.system.ISpCoreConfigurationStorage;
 import org.apache.streampipes.storage.couchdb.dao.AbstractDao;
 import org.apache.streampipes.storage.couchdb.utils.Utils;
 
-import java.util.List;
-
 public class CoreConfigurationStorageImpl extends 
AbstractDao<SpCoreConfiguration>
     implements ISpCoreConfigurationStorage {
 
-
   public CoreConfigurationStorageImpl() {
     super(Utils::getCouchDbGeneralConfigStorage, SpCoreConfiguration.class);
   }
 
   @Override
   public boolean exists() {
-    return !findAll().isEmpty();
-  }
-
-  @Override
-  public List<SpCoreConfiguration> getAll() {
-    return findAll();
+    return get() != null;
   }
 
   @Override
diff --git 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
 
b/ui/projects/streampipes/shared-ui/src/lib/models/sp-configuration-section.model.ts
similarity index 65%
copy from 
streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
copy to 
ui/projects/streampipes/shared-ui/src/lib/models/sp-configuration-section.model.ts
index 468bbf5a05..b79a0c255c 100644
--- 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/models/sp-configuration-section.model.ts
@@ -16,23 +16,17 @@
  *
  */
 
-package org.apache.streampipes.storage.api.system;
+import { Type } from '@angular/core';
 
-import org.apache.streampipes.model.configuration.SpCoreConfiguration;
-
-import java.util.List;
-
-public interface ISpCoreConfigurationStorage {
-
-  boolean exists();
-
-  List<SpCoreConfiguration> getAll();
-
-  void createElement(SpCoreConfiguration element);
-
-  SpCoreConfiguration get();
-
-  SpCoreConfiguration updateElement(SpCoreConfiguration element);
-
-  void deleteElement();
+interface SpConfigurationSectionBase {
+    itemId: string;
+    itemTitle: string;
+    roles: string[];
+    order?: number;
 }
+
+export type SpConfigurationSection = SpConfigurationSectionBase &
+    (
+        | { component: Type<unknown> }
+        | { loadComponent: () => Promise<Type<unknown>> }
+    );
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/services/configuration-sections.service.ts
 
b/ui/projects/streampipes/shared-ui/src/lib/services/configuration-sections.service.ts
new file mode 100644
index 0000000000..dff3baabe9
--- /dev/null
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/services/configuration-sections.service.ts
@@ -0,0 +1,104 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Injectable, inject, Type } from '@angular/core';
+import { TranslateService } from '@ngx-translate/core';
+import { SpNavigationItem } from '../models/sp-navigation.model';
+import { CurrentUserService } from './current-user.service';
+import { SpConfigurationSection } from 
'../models/sp-configuration-section.model';
+import { SP_CONFIGURATION_SECTIONS } from './configuration-sections.token';
+
+@Injectable({ providedIn: 'root' })
+export class SpConfigurationSectionsService {
+    private currentUserService = inject(CurrentUserService);
+    private translateService = inject(TranslateService);
+    private registeredSections =
+        inject(SP_CONFIGURATION_SECTIONS, { optional: true }) ?? [];
+
+    public getTabs(): SpNavigationItem[] {
+        return this.getVisibleSections().map(section => ({
+            itemId: section.itemId,
+            itemTitle: this.translateService.instant(section.itemTitle),
+            itemLink: ['configuration', section.itemId],
+            roles: section.roles,
+        }));
+    }
+
+    public getTabTitle(itemId: string): string {
+        const section = this.getSection(itemId);
+        return section
+            ? this.translateService.instant(section.itemTitle)
+            : itemId;
+    }
+
+    public getSection(itemId: string): SpConfigurationSection | undefined {
+        return this.getSections().find(section => section.itemId === itemId);
+    }
+
+    public async getSectionComponent(
+        itemId: string,
+    ): Promise<Type<unknown> | undefined> {
+        const section = this.getSection(itemId);
+        if (!section) {
+            return undefined;
+        }
+
+        if ('component' in section) {
+            return section.component;
+        }
+
+        if ('loadComponent' in section) {
+            return section.loadComponent();
+        }
+
+        return undefined;
+    }
+
+    public getDefaultTab(): SpNavigationItem | undefined {
+        return this.getTabs()[0];
+    }
+
+    public isTabActive(
+        activeTabs: SpNavigationItem[],
+        itemId: string,
+    ): boolean {
+        return activeTabs.some(tab => tab.itemId === itemId);
+    }
+
+    private getVisibleSections(): SpConfigurationSection[] {
+        return this.getSections().filter(section =>
+            this.currentUserService.hasAnyRole(section.roles),
+        );
+    }
+
+    private getSections(): SpConfigurationSection[] {
+        const sections = new Map<string, SpConfigurationSection>();
+        this.registeredSections.forEach(section =>
+            sections.set(section.itemId, section),
+        );
+
+        return Array.from(sections.values()).sort((left, right) => {
+            const leftOrder = left.order ?? Number.MAX_SAFE_INTEGER;
+            const rightOrder = right.order ?? Number.MAX_SAFE_INTEGER;
+            if (leftOrder === rightOrder) {
+                return left.itemId.localeCompare(right.itemId);
+            }
+            return leftOrder - rightOrder;
+        });
+    }
+}
diff --git 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
 
b/ui/projects/streampipes/shared-ui/src/lib/services/configuration-sections.token.ts
similarity index 63%
copy from 
streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
copy to 
ui/projects/streampipes/shared-ui/src/lib/services/configuration-sections.token.ts
index 468bbf5a05..a7f5286c4a 100644
--- 
a/streampipes-storage-api/src/main/java/org/apache/streampipes/storage/api/system/ISpCoreConfigurationStorage.java
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/services/configuration-sections.token.ts
@@ -16,23 +16,19 @@
  *
  */
 
-package org.apache.streampipes.storage.api.system;
-
-import org.apache.streampipes.model.configuration.SpCoreConfiguration;
-
-import java.util.List;
-
-public interface ISpCoreConfigurationStorage {
-
-  boolean exists();
-
-  List<SpCoreConfiguration> getAll();
-
-  void createElement(SpCoreConfiguration element);
-
-  SpCoreConfiguration get();
-
-  SpCoreConfiguration updateElement(SpCoreConfiguration element);
-
-  void deleteElement();
+import { InjectionToken, Provider } from '@angular/core';
+import { SpConfigurationSection } from 
'../models/sp-configuration-section.model';
+
+export const SP_CONFIGURATION_SECTIONS = new InjectionToken<
+    SpConfigurationSection[]
+>('SP_CONFIGURATION_SECTIONS');
+
+export function provideConfigurationSection(
+    section: SpConfigurationSection,
+): Provider {
+    return {
+        provide: SP_CONFIGURATION_SECTIONS,
+        useValue: section,
+        multi: true,
+    };
 }
diff --git a/ui/projects/streampipes/shared-ui/src/public-api.ts 
b/ui/projects/streampipes/shared-ui/src/public-api.ts
index 5b4a0b2060..446a78381e 100644
--- a/ui/projects/streampipes/shared-ui/src/public-api.ts
+++ b/ui/projects/streampipes/shared-ui/src/public-api.ts
@@ -62,8 +62,11 @@ export * from 
'./lib/components/feature-card-host/feature-card-meta-section/feat
 export * from 
'./lib/components/feature-card-host/feature-card-meta-creation/feature-card-meta-creation.component';
 
 export * from './lib/models/sp-navigation.model';
+export * from './lib/models/sp-configuration-section.model';
 
 export * from './lib/services/breadcrumb.service';
+export * from './lib/services/configuration-sections.service';
+export * from './lib/services/configuration-sections.token';
 export * from './lib/services/jwt-token-storage.service';
 export * from './lib/services/current-user.service';
 export * from './lib/services/echarts-toolbox.service';
diff --git a/ui/src/app/app.config.ts b/ui/src/app/app.config.ts
index 6f3a05bbaa..d2bdfa6643 100644
--- a/ui/src/app/app.config.ts
+++ b/ui/src/app/app.config.ts
@@ -49,6 +49,7 @@ import { provideTranslateHttpLoader } from 
'@ngx-translate/http-loader';
 import { NgxEchartsModule } from 'ngx-echarts';
 import { MatNativeDateModule } from '@angular/material/core';
 import { MonacoEditorModule } from 'ngx-monaco-editor-v2';
+import { CONFIGURATION_SECTION_PROVIDERS } from 
'./configuration/configuration-sections.providers';
 
 export const appConfig: ApplicationConfig = {
     providers: [
@@ -93,6 +94,7 @@ export const appConfig: ApplicationConfig = {
             provide: MatPaginatorIntl,
             useClass: PaginatorService,
         },
+        ...CONFIGURATION_SECTION_PROVIDERS,
         provideAnimations(),
     ],
 };
diff --git a/ui/src/app/configuration/configuration-route.guard.ts 
b/ui/src/app/configuration/configuration-route.guard.ts
index 312ccecc14..dfbdf2ab7b 100644
--- a/ui/src/app/configuration/configuration-route.guard.ts
+++ b/ui/src/app/configuration/configuration-route.guard.ts
@@ -20,15 +20,25 @@ import { inject } from '@angular/core';
 import { CanActivateFn, Router } from '@angular/router';
 import { SpConfigurationTabsService } from './configuration-tabs.service';
 
+export const configurationDefaultRouteGuard: CanActivateFn = () => {
+    const tabService = inject(SpConfigurationTabsService);
+    const router = inject(Router);
+    const defaultTab = tabService.getDefaultTab();
+    return defaultTab ? router.createUrlTree(defaultTab.itemLink) : false;
+};
+
 export const configurationRouteGuard: CanActivateFn = route => {
     const tabService = inject(SpConfigurationTabsService);
     const tabs = tabService.getTabs();
-    const router: Router = inject(Router);
-    const path = route.routeConfig.path;
-    if (tabService.isTabActive(tabs, path)) {
+    const router = inject(Router);
+    const configurationSectionId = 
route.paramMap.get('configurationSectionId');
+    if (
+        configurationSectionId &&
+        tabService.isTabActive(tabs, configurationSectionId)
+    ) {
         return true;
-    } else {
-        router.navigate(tabs[0].itemLink, { skipLocationChange: true });
-        return false;
     }
+
+    const defaultTab = tabService.getDefaultTab();
+    return defaultTab ? router.createUrlTree(defaultTab.itemLink) : false;
 };
diff --git a/ui/src/app/configuration/configuration-section-host.component.ts 
b/ui/src/app/configuration/configuration-section-host.component.ts
new file mode 100644
index 0000000000..999cea157d
--- /dev/null
+++ b/ui/src/app/configuration/configuration-section-host.component.ts
@@ -0,0 +1,59 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { NgComponentOutlet } from '@angular/common';
+import { Component, Type, inject } from '@angular/core';
+import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
+import { ActivatedRoute } from '@angular/router';
+import { map } from 'rxjs/operators';
+import { SpConfigurationTabsService } from './configuration-tabs.service';
+
+@Component({
+    selector: 'sp-configuration-section-host',
+    template: `
+        @if (sectionComponent) {
+            <ng-container *ngComponentOutlet="sectionComponent"></ng-container>
+        }
+    `,
+    imports: [NgComponentOutlet],
+})
+export class ConfigurationSectionHostComponent {
+    sectionComponent?: Type<unknown>;
+
+    private route = inject(ActivatedRoute);
+    private tabService = inject(SpConfigurationTabsService);
+
+    constructor() {
+        this.route.paramMap
+            .pipe(
+                map(params => params.get('configurationSectionId')),
+                takeUntilDestroyed(),
+            )
+            .subscribe(sectionId => {
+                void this.updateSectionComponent(sectionId);
+            });
+    }
+
+    private async updateSectionComponent(
+        sectionId: string | null,
+    ): Promise<void> {
+        this.sectionComponent = sectionId
+            ? await this.tabService.getSectionComponent(sectionId)
+            : undefined;
+    }
+}
diff --git a/ui/src/app/configuration/configuration-sections.providers.ts 
b/ui/src/app/configuration/configuration-sections.providers.ts
new file mode 100644
index 0000000000..bb7a387ab4
--- /dev/null
+++ b/ui/src/app/configuration/configuration-sections.providers.ts
@@ -0,0 +1,129 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ *
+ */
+
+import { Provider } from '@angular/core';
+import {
+    SpConfigurationSection,
+    provideConfigurationSection,
+} from '@streampipes/shared-ui';
+
+const CORE_CONFIGURATION_SECTIONS: SpConfigurationSection[] = [
+    {
+        itemId: 'general',
+        itemTitle: 'General',
+        roles: ['ROLE_ADMIN'],
+        loadComponent: () =>
+            
import('./general-configuration/general-configuration.component').then(
+                m => m.GeneralConfigurationComponent,
+            ),
+        order: 100,
+    },
+    {
+        itemId: 'export',
+        itemTitle: 'Export/Import',
+        roles: ['ROLE_ADMIN'],
+        loadComponent: () =>
+            import('./export/data-export-import.component').then(
+                m => m.SpDataExportImportComponent,
+            ),
+        order: 200,
+    },
+    {
+        itemId: 'extensions-installation',
+        itemTitle: 'Extensions',
+        roles: ['ROLE_ADMIN'],
+        loadComponent: () =>
+            
import('./extensions-installation/extensions-installation.component').then(
+                m => m.SpExtensionsInstallationComponent,
+            ),
+        order: 300,
+    },
+    {
+        itemId: 'extensions-services',
+        itemTitle: 'Extension Services',
+        roles: ['ROLE_ADMIN'],
+        loadComponent: () =>
+            
import('./extensions-service-management/extensions-service-management.component').then(
+                m => m.ExtensionsServiceManagementComponent,
+            ),
+        order: 400,
+    },
+    {
+        itemId: 'files',
+        itemTitle: 'Files',
+        roles: ['PRIVILEGE_WRITE_FILES'],
+        loadComponent: () =>
+            import('./files/files.component').then(m => m.FilesComponent),
+        order: 500,
+    },
+    {
+        itemId: 'labels',
+        itemTitle: 'Labels',
+        roles: ['PRIVILEGE_WRITE_LABELS'],
+        loadComponent: () =>
+            import('./label-configuration/label-configuration.component').then(
+                m => m.SpLabelConfigurationComponent,
+            ),
+        order: 600,
+    },
+    {
+        itemId: 'email',
+        itemTitle: 'Mail',
+        roles: ['ROLE_ADMIN'],
+        loadComponent: () =>
+            import('./email-configuration/email-configuration.component').then(
+                m => m.EmailConfigurationComponent,
+            ),
+        order: 700,
+    },
+    {
+        itemId: 'messaging',
+        itemTitle: 'Messaging',
+        roles: ['ROLE_ADMIN'],
+        loadComponent: () =>
+            
import('./messaging-configuration/messaging-configuration.component').then(
+                m => m.MessagingConfigurationComponent,
+            ),
+        order: 800,
+    },
+    {
+        itemId: 'security',
+        itemTitle: 'Security',
+        roles: ['ROLE_ADMIN'],
+        loadComponent: () =>
+            
import('./security-configuration/security-configuration.component').then(
+                m => m.SecurityConfigurationComponent,
+            ),
+        order: 900,
+    },
+    {
+        itemId: 'sites',
+        itemTitle: 'Sites',
+        roles: ['PRIVILEGE_WRITE_ASSETS'],
+        loadComponent: () =>
+            import('./sites-configuration/sites-configuration.component').then(
+                m => m.SitesConfigurationComponent,
+            ),
+        order: 1000,
+    },
+];
+
+export const CONFIGURATION_SECTION_PROVIDERS: Provider[] =
+    CORE_CONFIGURATION_SECTIONS.map(section =>
+        provideConfigurationSection(section),
+    );
diff --git a/ui/src/app/configuration/configuration-tabs.service.ts 
b/ui/src/app/configuration/configuration-tabs.service.ts
index 1c8c0558e9..f4620f8989 100644
--- a/ui/src/app/configuration/configuration-tabs.service.ts
+++ b/ui/src/app/configuration/configuration-tabs.service.ts
@@ -16,95 +16,43 @@
  *
  */
 
-import { SpNavigationItem } from '@streampipes/shared-ui';
-import { Injectable, inject } from '@angular/core';
-import { AuthService } from '../services/auth.service';
-import { TranslateService } from '@ngx-translate/core';
+import {
+    SpConfigurationSectionsService,
+    SpNavigationItem,
+} from '@streampipes/shared-ui';
+import { Injectable, Type, inject } from '@angular/core';
 
 @Injectable({ providedIn: 'root' })
 export class SpConfigurationTabsService {
-    private translateService = inject(TranslateService);
-    allConfigurationTabs: SpNavigationItem[] = [
-        {
-            itemId: 'general',
-            itemTitle: this.translateService.instant('General'),
-            itemLink: ['configuration', 'general'],
-            roles: ['ROLE_ADMIN'],
-        },
-        {
-            itemId: 'export',
-            itemTitle: this.translateService.instant('Export/Import'),
-            itemLink: ['configuration', 'export'],
-            roles: ['ROLE_ADMIN'],
-        },
-        {
-            itemId: 'extensions-installation',
-            itemTitle: this.translateService.instant('Extensions'),
-            itemLink: ['configuration', 'extensions-installation'],
-            roles: ['ROLE_ADMIN'],
-        },
-        {
-            itemId: 'extensions-services',
-            itemTitle: this.translateService.instant('Extension Services'),
-            itemLink: ['configuration', 'extensions-services'],
-            roles: ['ROLE_ADMIN'],
-        },
-        {
-            itemId: 'files',
-            itemTitle: this.translateService.instant('Files'),
-            itemLink: ['configuration', 'files'],
-            roles: ['PRIVILEGE_WRITE_FILES'],
-        },
-        {
-            itemId: 'labels',
-            itemTitle: this.translateService.instant('Labels'),
-            itemLink: ['configuration', 'labels'],
-            roles: ['PRIVILEGE_WRITE_LABELS'],
-        },
-        {
-            itemId: 'email',
-            itemTitle: this.translateService.instant('Mail'),
-            itemLink: ['configuration', 'email'],
-            roles: ['ROLE_ADMIN'],
-        },
-        {
-            itemId: 'messaging',
-            itemTitle: this.translateService.instant('Messaging'),
-            itemLink: ['configuration', 'messaging'],
-            roles: ['ROLE_ADMIN'],
-        },
-        {
-            itemId: 'security',
-            itemTitle: this.translateService.instant('Security'),
-            itemLink: ['configuration', 'security'],
-            roles: ['ROLE_ADMIN'],
-        },
-        {
-            itemId: 'sites',
-            itemTitle: this.translateService.instant('Sites'),
-            itemLink: ['configuration', 'sites'],
-            roles: ['PRIVILEGE_WRITE_ASSETS'],
-        },
-    ];
-
-    constructor(private authService: AuthService) {}
+    private configurationSectionsService = inject(
+        SpConfigurationSectionsService,
+    );
 
     public getTabs(): SpNavigationItem[] {
-        return this.allConfigurationTabs.filter(c =>
-            this.authService.hasAnyRole(c.roles),
-        );
+        return this.configurationSectionsService.getTabs();
     }
 
     public getTabTitle(itemId: string): string {
-        return this.allConfigurationTabs.find(t => t.itemId === itemId)
-            .itemTitle;
+        return this.configurationSectionsService.getTabTitle(itemId);
+    }
+
+    public getSectionComponent(
+        itemId: string,
+    ): Promise<Type<unknown> | undefined> {
+        return this.configurationSectionsService.getSectionComponent(itemId);
+    }
+
+    public getDefaultTab(): SpNavigationItem | undefined {
+        return this.configurationSectionsService.getDefaultTab();
     }
 
     public isTabActive(
         activeTabs: SpNavigationItem[],
         itemId: string,
     ): boolean {
-        const links = activeTabs.map(tab => tab.itemLink[1]);
-        return links.indexOf(itemId) !== -1;
+        return this.configurationSectionsService.isTabActive(
+            activeTabs,
+            itemId,
+        );
     }
 }
diff --git a/ui/src/app/configuration/configuration.routes.ts 
b/ui/src/app/configuration/configuration.routes.ts
index ef7910790a..1473e354ee 100644
--- a/ui/src/app/configuration/configuration.routes.ts
+++ b/ui/src/app/configuration/configuration.routes.ts
@@ -17,21 +17,15 @@
  */
 
 import { Routes } from '@angular/router';
-import { GeneralConfigurationComponent } from 
'./general-configuration/general-configuration.component';
-import { configurationRouteGuard } from './configuration-route.guard';
-import { EmailConfigurationComponent } from 
'./email-configuration/email-configuration.component';
-import { SpDataExportImportComponent } from 
'./export/data-export-import.component';
-import { SpLabelConfigurationComponent } from 
'./label-configuration/label-configuration.component';
-import { MessagingConfigurationComponent } from 
'./messaging-configuration/messaging-configuration.component';
-import { SpExtensionsInstallationComponent } from 
'./extensions-installation/extensions-installation.component';
-import { ExtensionsServiceManagementComponent } from 
'./extensions-service-management/extensions-service-management.component';
-import { FilesComponent } from './files/files.component';
-import { SecurityConfigurationComponent } from 
'./security-configuration/security-configuration.component';
-import { SitesConfigurationComponent } from 
'./sites-configuration/sites-configuration.component';
+import {
+    configurationDefaultRouteGuard,
+    configurationRouteGuard,
+} from './configuration-route.guard';
 import { OrderByPipe } from './extensions-installation/filter/order-by.pipe';
 import { PipelineElementInstallationStatusFilter } from 
'./extensions-installation/filter/pipeline-element-installation-status.pipe';
 import { PipelineElementNameFilter } from 
'./extensions-installation/filter/pipeline-element-name.pipe';
 import { PipelineElementTypeFilter } from 
'./extensions-installation/filter/pipeline-element-type.pipe';
+import { ConfigurationSectionHostComponent } from 
'./configuration-section-host.component';
 
 export const CONFIGURATION_ROUTES: Routes = [
     {
@@ -39,57 +33,13 @@ export const CONFIGURATION_ROUTES: Routes = [
         children: [
             {
                 path: '',
-                redirectTo: 'general',
                 pathMatch: 'full',
+                component: ConfigurationSectionHostComponent,
+                canActivate: [configurationDefaultRouteGuard],
             },
             {
-                path: 'general',
-                component: GeneralConfigurationComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'email',
-                component: EmailConfigurationComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'export',
-                component: SpDataExportImportComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'labels',
-                component: SpLabelConfigurationComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'messaging',
-                component: MessagingConfigurationComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'extensions-installation',
-                component: SpExtensionsInstallationComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'extensions-services',
-                component: ExtensionsServiceManagementComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'files',
-                component: FilesComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'security',
-                component: SecurityConfigurationComponent,
-                canActivate: [configurationRouteGuard],
-            },
-            {
-                path: 'sites',
-                component: SitesConfigurationComponent,
+                path: ':configurationSectionId',
+                component: ConfigurationSectionHostComponent,
                 canActivate: [configurationRouteGuard],
             },
         ],

Reply via email to