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 11b7168d1e fix: Consistent Asset Create Workflow (#4644)
11b7168d1e is described below

commit 11b7168d1e7c9acfda3e231c4f0605b2aaf3fab5
Author: Jacqueline Höllig <[email protected]>
AuthorDate: Sat Jun 27 22:25:54 2026 +0200

    fix: Consistent Asset Create Workflow (#4644)
---
 ui/cypress/support/utils/asset/AssetBtns.ts        |   3 +
 ui/cypress/support/utils/asset/AssetUtils.ts       |   4 +-
 ui/deployment/i18n/de.json                         |   3 +-
 ui/deployment/i18n/en.json                         |   3 +-
 ui/deployment/i18n/pl.json                         |   3 +-
 .../asset-details/base-asset-details.directive.ts  |  29 ++++-
 .../edit-asset/asset-details.component.html        |   8 +-
 .../edit-asset/asset-details.component.ts          | 120 +++++++++++++++------
 .../asset-overview/asset-overview.component.ts     |  21 +---
 .../create-asset-dialog.component.html             |  69 ------------
 .../create-asset/create-asset-dialog.component.ts  |  72 -------------
 11 files changed, 134 insertions(+), 201 deletions(-)

diff --git a/ui/cypress/support/utils/asset/AssetBtns.ts 
b/ui/cypress/support/utils/asset/AssetBtns.ts
index fc937cc33b..ff867b497b 100644
--- a/ui/cypress/support/utils/asset/AssetBtns.ts
+++ b/ui/cypress/support/utils/asset/AssetBtns.ts
@@ -22,6 +22,9 @@ export class AssetBtns {
     public static createAssetBtn() {
         return cy.dataCy('create-new-asset-button', { timeout: 10000 });
     }
+    public static createBtn() {
+        return cy.dataCy('sp-manage-save', { timeout: 10000 });
+    }
 
     public static assetNameInput() {
         return cy.dataCy('asset-name', { timeout: 10000 });
diff --git a/ui/cypress/support/utils/asset/AssetUtils.ts 
b/ui/cypress/support/utils/asset/AssetUtils.ts
index 2b6918a6ff..c2f98b9319 100644
--- a/ui/cypress/support/utils/asset/AssetUtils.ts
+++ b/ui/cypress/support/utils/asset/AssetUtils.ts
@@ -89,13 +89,14 @@ export class AssetUtils {
         AssetUtils.addNewAsset(asset);
 
         AssetBtns.saveAssetBtn().click();
+        AssetBtns.createBtn().click();
+
         AssetBtns.createAssetBtn().should('be.visible');
     }
 
     public static addNewAsset(asset: Asset) {
         AssetBtns.createAssetBtn().click();
         AssetBtns.assetNameInput().clear().type(asset.name);
-        AssetBtns.createAssetPanelBtn().click();
 
         this.selectAssetType(asset.assetType);
         if (asset.site) {
@@ -265,6 +266,7 @@ export class AssetUtils {
 
         AssetUtils.checkAmountOfLinkedResources(2);
         AssetBtns.saveAssetBtn().click();
+        AssetBtns.createBtn().click();
         cy.location('hash', { timeout: 10000 }).should(
             'include',
             '/assets/overview',
diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json
index fcb8e4df5d..d6ebc2dc3b 100644
--- a/ui/deployment/i18n/de.json
+++ b/ui/deployment/i18n/de.json
@@ -115,7 +115,6 @@
   "Areas": "Bereiche",
   "Asset": "Asset",
   "Asset Context": "Asset-Kontext",
-  "Asset Name": "Asset Name",
   "Asset Type": "Asset-Typ",
   "Asset links": "Asset-Links",
   "Assets": "Assets",
@@ -241,7 +240,6 @@
   "Create a new asset in the asset view before adding it to a resource.": 
"Erstellen Sie zuerst ein neues Asset in der Asset-Ansicht, bevor Sie es zu 
einer Ressource hinzufügen.",
   "Create a new group in the Groups section of the security page.": "Erstellen 
Sie eine neue Gruppe im Abschnitt Gruppen auf der Sicherheitsseite.",
   "Create adapter": "Adapter erstellen",
-  "Create asset": "Asset erstellen",
   "Create asset links": "Asset-Links erstellen",
   "Create chart": "Diagramm erstellen",
   "Create chart in new tab": "Diagramm in neuem Tab erstellen",
@@ -704,6 +702,7 @@
   "New": "Neu",
   "New API key": "Neuer API-Schlüssel",
   "New Alternate ID": "Neue alternative ID",
+  "New Asset": "Neues Asset",
   "New Export Provider": "Neuer Exportanbieter",
   "New Pipeline": "Neue Pipeline",
   "New adapter": "Neuer Adapter",
diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json
index 7b36992eac..bb65b520b2 100644
--- a/ui/deployment/i18n/en.json
+++ b/ui/deployment/i18n/en.json
@@ -115,7 +115,6 @@
   "Areas": null,
   "Asset": null,
   "Asset Context": null,
-  "Asset Name": null,
   "Asset Type": null,
   "Asset links": null,
   "Assets": null,
@@ -241,7 +240,6 @@
   "Create a new asset in the asset view before adding it to a resource.": null,
   "Create a new group in the Groups section of the security page.": null,
   "Create adapter": null,
-  "Create asset": null,
   "Create asset links": null,
   "Create chart": null,
   "Create chart in new tab": null,
@@ -704,6 +702,7 @@
   "New": null,
   "New API key": null,
   "New Alternate ID": null,
+  "New Asset": null,
   "New Export Provider": null,
   "New Pipeline": null,
   "New adapter": null,
diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json
index c04a0d71b1..cf392018df 100644
--- a/ui/deployment/i18n/pl.json
+++ b/ui/deployment/i18n/pl.json
@@ -115,7 +115,6 @@
   "Areas": "Obszary",
   "Asset": "Zasób",
   "Asset Context": "Kontekst zasobu",
-  "Asset Name": "Nazwa zasobu",
   "Asset Type": "Typ zasobu",
   "Asset links": "Linki zasobu",
   "Assets": "Zasoby",
@@ -241,7 +240,6 @@
   "Create a new asset in the asset view before adding it to a resource.": 
"Utwórz nowy zasób w widoku zasobów, zanim dodasz go do zasobu systemowego.",
   "Create a new group in the Groups section of the security page.": "Utwórz 
nową grupę w sekcji Grupy na stronie bezpieczeństwa.",
   "Create adapter": "Utwórz adapter",
-  "Create asset": "Utwórz zasób",
   "Create asset links": "Utwórz linki zasobu",
   "Create chart": "Utwórz wykres",
   "Create chart in new tab": "Utwórz wykres w nowej karcie",
@@ -704,6 +702,7 @@
   "New": "Nowy",
   "New API key": "Nowy klucz API",
   "New Alternate ID": "Nowe alternatywne ID",
+  "New Asset": "Nowy składnik aktywów",
   "New Export Provider": "Nowy dostawca eksportu",
   "New Pipeline": "Nowy strumień",
   "New adapter": "Nowy adapter",
diff --git 
a/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts 
b/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
index 2e45bc314d..c2e88960ca 100644
--- a/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
+++ b/ui/src/app/assets/components/asset-details/base-asset-details.directive.ts
@@ -27,7 +27,7 @@ import {
     SpAssetModel,
 } from '@streampipes/platform-services';
 import { ActivatedRoute } from '@angular/router';
-import { zip } from 'rxjs';
+import { of, zip } from 'rxjs';
 import { SpAssetRoutes } from '../../assets.breadcrumb';
 
 @Directive()
@@ -39,6 +39,7 @@ export abstract class BaseAssetDetailsDirective implements 
OnInit {
     rootNode = true;
 
     assetModelId: string;
+    protected isNewAsset = false;
 
     private breadcrumbService = inject(SpBreadcrumbService);
     protected route = inject(ActivatedRoute);
@@ -60,7 +61,10 @@ export abstract class BaseAssetDetailsDirective implements 
OnInit {
     }
 
     loadResources(): void {
-        const assetReq = this.assetService.getAsset(this.assetModelId);
+        const newAsset = this.getNewAssetFromNavigationState();
+        const assetReq = newAsset
+            ? of(newAsset)
+            : this.assetService.getAsset(this.assetModelId);
         const locationsReq = this.genericStorageService.getAllDocuments(
             AssetConstants.ASSET_SITES_APP_DOC_NAME,
         );
@@ -82,6 +86,12 @@ export abstract class BaseAssetDetailsDirective implements 
OnInit {
     }
 
     applySites(): void {
+        this.asset.assetSite ??= {
+            area: undefined,
+            siteId: undefined,
+            hasExactLocation: false,
+            location: undefined,
+        };
         if (!this.asset.assetSite.hasExactLocation) {
             const matchingSite = this.sites.find(
                 site => site._id === this.asset.assetSite.siteId,
@@ -98,4 +108,19 @@ export abstract class BaseAssetDetailsDirective implements 
OnInit {
     }
 
     abstract onAssetAvailable(): void;
+
+    private getNewAssetFromNavigationState(): SpAssetModel | undefined {
+        const state = history.state as {
+            assetModel?: SpAssetModel;
+            isNewAsset?: boolean;
+        };
+        if (
+            state?.isNewAsset &&
+            state.assetModel?.elementId === this.assetModelId
+        ) {
+            this.isNewAsset = true;
+            return state.assetModel;
+        }
+        return undefined;
+    }
 }
diff --git 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.html
 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.html
index 3edca7e368..1dbc66d3f4 100644
--- 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.html
+++ 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.html
@@ -45,7 +45,13 @@
                             (click)="saveAsset()"
                         >
                             <i class="material-icons">save</i
-                            ><span>&nbsp;{{ 'Save' | translate }}</span>
+                            ><span
+                                >&nbsp;{{
+                                    isNewAsset
+                                        ? ('Create' | translate)
+                                        : ('Save' | translate)
+                                }}</span
+                            >
                         </button>
                     </div>
                 </div>
diff --git 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
index 2a4276160e..3abea23a87 100644
--- 
a/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
+++ 
b/ui/src/app/assets/components/asset-details/edit-asset/asset-details.component.ts
@@ -51,7 +51,7 @@ import {
 } from '@streampipes/platform-services';
 import { MatDialog } from '@angular/material/dialog';
 import { firstValueFrom, from, Observable, of } from 'rxjs';
-import { map, switchMap } from 'rxjs/operators';
+import { map, switchMap, tap } from 'rxjs/operators';
 import { SupportsUnsavedChangeDialog } from 
'../../../../chart-shared/models/dataview-dashboard.model';
 
 type ManageableAsset = SpAssetModel & {
@@ -93,6 +93,10 @@ export class SpAssetDetailsComponent
     private originalAsset: SpAssetModel;
 
     async saveAsset() {
+        if (this.isNewAsset && this.pendingManageAssetResult === undefined) {
+            this.openManageAssetDialog(true);
+            return;
+        }
         await this.saveAssetChanges();
         this.assetBrowserService.refreshBrowserAssetData();
         this.router.navigate(['assets'], {
@@ -110,41 +114,17 @@ export class SpAssetDetailsComponent
     }
 
     manageAsset(): void {
-        const resource = this.makeManageableAsset(this.asset);
-        const resourceConfig: 
ObjectManageDialogResourceConfig<ManageableAsset> =
-            {
-                resourceLabel: 'Asset',
-                nameLabel: 'Asset name',
-                descriptionLabel: 'Asset description',
-                nameProperty: 'name',
-                showAssetLinking: false,
-            };
-        const dialogRef = this.dialogService.open(ObjectManageDialogComponent, 
{
-            panelType: PanelType.SLIDE_IN_PANEL,
-            title: this.translateService.instant('Manage'),
-            width: '50vw',
-            data: {
-                objectInstanceId: resource.elementId,
-                resource,
-                saveMode: 'deferred',
-                resourceConfig,
-                headerTitle:
-                    this.translateService.instant('Manage Asset ') +
-                    resource.name,
-            },
-        });
-        dialogRef.afterClosed().subscribe(result => {
-            if (result && typeof result !== 'boolean') {
-                this.pendingManageAssetResult = result;
-                Object.assign(
-                    this.asset,
-                    this.makeAssetResource(result.resource),
-                );
-            }
-        });
+        this.openManageAssetDialog();
     }
 
     deleteAsset(): void {
+        if (this.isNewAsset) {
+            this.router.navigate(['assets'], {
+                state: { omitConfirm: true },
+            });
+            return;
+        }
+
         const dialogRef = this.dialog.open(ConfirmDialogComponent, {
             width: '500px',
             data: {
@@ -172,6 +152,73 @@ export class SpAssetDetailsComponent
         });
     }
 
+    private openManageAssetDialog(saveAfterClose = false): void {
+        const resource = this.makeManageableAsset(this.asset);
+        const resourceConfig: 
ObjectManageDialogResourceConfig<ManageableAsset> =
+            {
+                resourceLabel: 'Asset',
+                nameLabel: 'Asset name',
+                descriptionLabel: 'Asset description',
+                nameProperty: 'name',
+                showAssetLinking: false,
+                saveResource: this.isNewAsset
+                    ? resource =>
+                          this.assetService
+                              .createAsset(this.makeAssetResource(resource))
+                              .pipe(
+                                  tap(savedAsset => {
+                                      Object.assign(
+                                          this.asset,
+                                          savedAsset ??
+                                              this.makeAssetResource(resource),
+                                      );
+                                      this.isNewAsset = false;
+                                  }),
+                              )
+                    : undefined,
+            };
+        const dialogRef = this.dialogService.open(ObjectManageDialogComponent, 
{
+            panelType: PanelType.SLIDE_IN_PANEL,
+            title: this.isNewAsset
+                ? this.translateService.instant('New Asset')
+                : this.translateService.instant('Manage'),
+            width: '50vw',
+
+            data: {
+                objectInstanceId: resource.elementId,
+                resource,
+                saveMode: this.isNewAsset ? 'immediate' : 'deferred',
+                createMode: this.isNewAsset,
+                resourceConfig,
+                headerTitle: this.isNewAsset
+                    ? this.translateService.instant('New Asset')
+                    : this.translateService.instant('Manage Asset ') +
+                      (resource.name ?? ''),
+            },
+        });
+        dialogRef.afterClosed().subscribe(result => {
+            if (saveAfterClose && result === true) {
+                this.assetBrowserService.refreshBrowserAssetData();
+                this.router.navigate(['assets'], {
+                    state: { omitConfirm: true },
+                });
+                return;
+            }
+
+            if (result && typeof result !== 'boolean') {
+                this.pendingManageAssetResult = result;
+                Object.assign(
+                    this.asset,
+                    this.makeAssetResource(result.resource),
+                );
+
+                if (saveAfterClose) {
+                    void this.saveAsset();
+                }
+            }
+        });
+    }
+
     confirmLeaveDialog(
         _route: ActivatedRouteSnapshot,
         _state: RouterStateSnapshot,
@@ -240,7 +287,12 @@ export class SpAssetDetailsComponent
 
     private async saveAssetChanges(): Promise<void> {
         this.cleanupEmpty();
-        await firstValueFrom(this.assetService.updateAsset(this.asset));
+        if (this.isNewAsset) {
+            await firstValueFrom(this.assetService.createAsset(this.asset));
+            this.isNewAsset = false;
+        } else {
+            await firstValueFrom(this.assetService.updateAsset(this.asset));
+        }
         await this.savePendingManageAssetChanges();
         this.originalAsset = this.cloneAsset(this.asset);
     }
diff --git 
a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts 
b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
index 0dab1654a3..92a403fc1c 100644
--- a/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
+++ b/ui/src/app/assets/components/asset-overview/asset-overview.component.ts
@@ -46,7 +46,6 @@ import {
 } from '@streampipes/shared-ui';
 import { SpAssetRoutes } from '../../assets.breadcrumb';
 import { Router } from '@angular/router';
-import { SpCreateAssetDialogComponent } from 
'../../dialog/create-asset/create-asset-dialog.component';
 import { IdGeneratorService } from 
'../../../core-services/id-generator/id-generator.service';
 import { UserPrivilege } from '../../../core/auth/user-privilege.enum';
 import { MatDialog } from '@angular/material/dialog';
@@ -196,25 +195,15 @@ export class SpAssetOverviewComponent implements OnInit {
             additionalData: {},
             labelIds: [],
         };
-        const dialogRef = this.dialogService.open(
-            SpCreateAssetDialogComponent,
+        this.router.navigate(
+            ['assets', 'details', assetModel.elementId, 'edit'],
             {
-                panelType: PanelType.SLIDE_IN_PANEL,
-                title: this.translateService.instant('Create asset'),
-                width: '50vw',
-                data: {
-                    assetModel: assetModel,
+                state: {
+                    assetModel,
+                    isNewAsset: true,
                 },
             },
         );
-
-        dialogRef.afterClosed().subscribe(ev => {
-            if (ev) {
-                this.loadAssets();
-                this.assetBrowserService.refreshBrowserAssetData();
-                this.goToDetailsView(assetModel, true);
-            }
-        });
     }
 
     goToDetailsView(asset: AssetSummaryDto, editMode = false) {
diff --git 
a/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.html 
b/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.html
deleted file mode 100644
index 67c2d43431..0000000000
--- a/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.html
+++ /dev/null
@@ -1,69 +0,0 @@
-<!--
-~ 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.
-~
--->
-
-<div class="sp-dialog-container">
-    <div class="sp-dialog-content p-15">
-        <div fxFlex="100">
-            <div
-                fxFlex="100"
-                fxLayout="column"
-                style="margin: 5px; width: 100%"
-            >
-                <sp-form-field [level]="2" [label]="'Asset Name' | translate">
-                    <mat-form-field>
-                        <input
-                            matInput
-                            data-cy="asset-name"
-                            [(ngModel)]="assetModel.assetName"
-                        />
-                    </mat-form-field>
-                </sp-form-field>
-                <sp-form-field [level]="2" [label]="'Description' | translate">
-                    <mat-form-field>
-                        <input
-                            matInput
-                            [(ngModel)]="assetModel.assetDescription"
-                        />
-                    </mat-form-field>
-                </sp-form-field>
-            </div>
-        </div>
-    </div>
-    <mat-divider></mat-divider>
-    <div class="sp-dialog-actions actions-align-left" fxLayoutGap="10px">
-        <button
-            mat-button
-            mat-flat-button
-            color="accent"
-            data-cy="create-asset-panel"
-            (click)="onSave()"
-        >
-            {{ 'Create' | translate }}
-        </button>
-
-        <button
-            mat-button
-            mat-flat-button
-            class="mat-basic mr-10"
-            (click)="onCancel()"
-            style="margin-right: 10px"
-        >
-            {{ 'Close' | translate }}
-        </button>
-    </div>
-</div>
diff --git 
a/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.ts 
b/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.ts
deleted file mode 100644
index e7747f8fa0..0000000000
--- a/ui/src/app/assets/dialog/create-asset/create-asset-dialog.component.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * 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 { Component, Input, inject } from '@angular/core';
-import {
-    AssetManagementService,
-    SpAssetModel,
-} from '@streampipes/platform-services';
-import { DialogRef } from '@streampipes/shared-ui';
-import {
-    FlexDirective,
-    LayoutDirective,
-    LayoutGapDirective,
-} from '@ngbracket/ngx-layout/flex';
-import { FormFieldComponent } from '@streampipes/shared-ui';
-import { MatFormField } from '@angular/material/form-field';
-import { MatInput } from '@angular/material/input';
-import { FormsModule } from '@angular/forms';
-import { MatDivider } from '@angular/material/divider';
-import { MatButton } from '@angular/material/button';
-import { TranslatePipe } from '@ngx-translate/core';
-
-@Component({
-    selector: 'sp-create-asset-dialog-component',
-    templateUrl: './create-asset-dialog.component.html',
-    imports: [
-        FlexDirective,
-        LayoutDirective,
-        FormFieldComponent,
-        MatFormField,
-        MatInput,
-        FormsModule,
-        MatDivider,
-        LayoutGapDirective,
-        MatButton,
-        TranslatePipe,
-    ],
-})
-export class SpCreateAssetDialogComponent {
-    private dialogRef =
-        inject<DialogRef<SpCreateAssetDialogComponent>>(DialogRef);
-    private assetManagementService = inject(AssetManagementService);
-
-    @Input() assetModel: SpAssetModel;
-
-    onCancel(): void {
-        this.dialogRef.close();
-    }
-
-    onSave(): void {
-        this.assetManagementService
-            .createAsset(this.assetModel)
-            .subscribe(() => {
-                this.dialogRef.close(true);
-            });
-    }
-}

Reply via email to