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

zehnder pushed a commit to branch 
4270-add-cancel-navigation-option-to-unsaved-changes-warning-for-chartsdashboards
in repository https://gitbox.apache.org/repos/asf/streampipes.git

commit 7892d0d7e4823626b7d2bb50564c8a69beec7793
Author: Philipp Zehnder <[email protected]>
AuthorDate: Fri Mar 20 15:38:01 2026 +0100

    fix(#4270): add keep editing option to leave dialog
---
 ui/deployment/i18n/de.json                         |  1 +
 ui/deployment/i18n/en.json                         |  1 +
 ui/deployment/i18n/pl.json                         |  1 +
 .../confirm-dialog/confirm-dialog.component.html   | 52 ++++++++++++++++------
 .../confirm-dialog/confirm-dialog.component.scss   |  4 ++
 .../confirm-dialog/confirm-dialog.component.ts     |  8 +++-
 .../components/chart-view/chart-view.component.ts  | 25 +++++++----
 .../components/panel/dashboard-panel.component.ts  | 21 ++++++---
 8 files changed, 82 insertions(+), 31 deletions(-)

diff --git a/ui/deployment/i18n/de.json b/ui/deployment/i18n/de.json
index 4b2c860442..62b990cf10 100644
--- a/ui/deployment/i18n/de.json
+++ b/ui/deployment/i18n/de.json
@@ -847,6 +847,7 @@
   "Save changes?": "Änderungen speichern?",
   "Update all changes to dashboard charts or discard current changes.": 
"Aktualisieren Sie alle Änderungen der Diagramme oder verwerfen Sie aktuelle 
Änderungen.",
   "Discard changes": "Änderungen verwerfen",
+  "Keep editing": "Weiter bearbeiten",
   "Off": "Aus",
   "Are you sure you want to delete this dashboard?": "Sind Sie sicher, dass 
Sie dieses Dashboard löschen möchten?",
   "This action cannot be undone!": "Diese Aktion kann nicht rückgängig gemacht 
werden!",
diff --git a/ui/deployment/i18n/en.json b/ui/deployment/i18n/en.json
index 9570d3407a..4d33544b20 100644
--- a/ui/deployment/i18n/en.json
+++ b/ui/deployment/i18n/en.json
@@ -847,6 +847,7 @@
   "Save changes?": null,
   "Update all changes to dashboard charts or discard current changes.": null,
   "Discard changes": null,
+  "Keep editing": null,
   "Off": null,
   "Are you sure you want to delete this dashboard?": null,
   "This action cannot be undone!": null,
diff --git a/ui/deployment/i18n/pl.json b/ui/deployment/i18n/pl.json
index 5420260fbc..591f1fa683 100644
--- a/ui/deployment/i18n/pl.json
+++ b/ui/deployment/i18n/pl.json
@@ -847,6 +847,7 @@
   "Save changes?": "Zapisać zmiany?",
   "Update all changes to dashboard charts or discard current changes.": 
"Zapisz wszystkie zmiany wykresów pulpitu lub odrzuć bieżące zmiany.",
   "Discard changes": "Odrzuć zmiany",
+  "Keep editing": "Kontynuuj edycję",
   "Off": "Wyłączone",
   "Are you sure you want to delete this dashboard?": "Czy na pewno chcesz 
usunąć ten pulpit?",
   "This action cannot be undone!": "Tego działania nie można cofnąć!",
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.html
 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.html
index 0a898a2cc8..96d592c0aa 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.html
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.html
@@ -24,21 +24,47 @@
         </div>
     </mat-dialog-content>
     <mat-dialog-actions>
-        <div fxFlex="100" fxLayoutAlign="end center" class="footer">
-            @if (data.confirmAndCancel) {
-                <button mat-button (click)="onCancel()" 
data-cy="cancel-delete">
-                    {{ data.cancelTitle }}
-                </button>
+        <div
+            fxFlex="100"
+            fxLayout="row"
+            fxLayoutAlign="start center"
+            class="footer"
+        >
+            @if (data.neutralTitle) {
+                <div class="neutral-action-wrapper">
+                    <button
+                        mat-button
+                        (click)="onNeutral()"
+                        data-cy="neutral-action"
+                    >
+                        {{ data.neutralTitle }}
+                    </button>
+                </div>
             }
-            <button
-                mat-flat-button
-                color="accent"
-                (click)="onOk()"
-                cdkFocusInitial
-                data-cy="confirm-delete"
+            <div
+                fxLayout="row"
+                fxLayoutAlign="end center"
+                class="primary-actions"
             >
-                {{ data.okTitle }}
-            </button>
+                @if (data.confirmAndCancel) {
+                    <button
+                        mat-button
+                        (click)="onCancel()"
+                        data-cy="cancel-delete"
+                    >
+                        {{ data.cancelTitle }}
+                    </button>
+                }
+                <button
+                    mat-flat-button
+                    color="accent"
+                    (click)="onOk()"
+                    cdkFocusInitial
+                    data-cy="confirm-delete"
+                >
+                    {{ data.okTitle }}
+                </button>
+            </div>
         </div>
     </mat-dialog-actions>
 </div>
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.scss
 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.scss
index 2f93cd02a0..eef4ad0780 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.scss
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.scss
@@ -19,3 +19,7 @@
 .footer {
     margin-top: 20px;
 }
+
+.neutral-action-wrapper {
+    margin-right: auto;
+}
diff --git 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.ts
 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.ts
index 9ee80e3e70..317c2fb343 100644
--- 
a/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.ts
+++ 
b/ui/projects/streampipes/shared-ui/src/lib/dialog/confirm-dialog/confirm-dialog.component.ts
@@ -50,10 +50,14 @@ export class ConfirmDialogComponent {
     ) {}
 
     onCancel(): void {
-        this.dialogRef.close();
+        this.dialogRef.close(this.data.cancelResult);
+    }
+
+    onNeutral(): void {
+        this.dialogRef.close(this.data.neutralResult);
     }
 
     onOk(): void {
-        this.dialogRef.close(true);
+        this.dialogRef.close(this.data.okResult ?? true);
     }
 }
diff --git a/ui/src/app/chart/components/chart-view/chart-view.component.ts 
b/ui/src/app/chart/components/chart-view/chart-view.component.ts
index 937d43fccf..9f0eee7aa4 100644
--- a/ui/src/app/chart/components/chart-view/chart-view.component.ts
+++ b/ui/src/app/chart/components/chart-view/chart-view.component.ts
@@ -57,7 +57,7 @@ import { ChartDetectChangesService } from 
'../../services/chart-detect-changes.s
 import { SupportsUnsavedChangeDialog } from 
'../../../chart-shared/models/dataview-dashboard.model';
 import { Observable, of, Subscription } from 'rxjs';
 import { MatDialog } from '@angular/material/dialog';
-import { catchError, map } from 'rxjs/operators';
+import { catchError, map, switchMap } from 'rxjs/operators';
 import { TranslatePipe, TranslateService } from '@ngx-translate/core';
 import { ResizeEchartsService } from 
'../../../chart-shared/services/resize-echarts.service';
 import { AssetDialogComponent } from '../../dialog/asset-dialog.component';
@@ -428,27 +428,34 @@ export class ChartViewComponent
                     subtitle: this.translateService.instant(
                         'Update all changes to chart or discard current 
changes.',
                     ),
+                    neutralTitle: this.translateService.instant('Keep 
editing'),
+                    neutralResult: 'stay',
                     cancelTitle:
                         this.translateService.instant('Discard changes'),
+                    cancelResult: false,
                     okTitle: this.translateService.instant('Update'),
+                    okResult: true,
                     confirmAndCancel: true,
                 },
             });
             return dialogRef.afterClosed().pipe(
-                map(shouldUpdate => {
-                    if (shouldUpdate) {
+                switchMap(dialogResult => {
+                    if (dialogResult === true) {
                         this.dataView.timeSettings = this.timeSettings;
-                        const observable =
+                        return (
                             this.dataView.elementId !== undefined
                                 ? this.dataViewService.updateChart(
                                       this.dataView,
                                   )
-                                : 
this.dataViewService.saveChart(this.dataView);
-                        observable.subscribe(() => {
-                            return true;
-                        });
+                                : this.dataViewService.saveChart(this.dataView)
+                        ).pipe(map(() => true));
                     }
-                    return true;
+
+                    if (dialogResult === false) {
+                        return of(true);
+                    }
+
+                    return of(false);
                 }),
             );
         } else {
diff --git a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts 
b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
index 812959d7a3..a7f6e49ebf 100644
--- a/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
+++ b/ui/src/app/dashboard/components/panel/dashboard-panel.component.ts
@@ -325,24 +325,31 @@ export class DashboardPanelComponent
                     subtitle: this.translateService.instant(
                         'Update all changes to dashboard charts or discard 
current changes.',
                     ),
+                    neutralTitle: this.translateService.instant('Keep 
editing'),
+                    neutralResult: 'stay',
                     cancelTitle:
                         this.translateService.instant('Discard changes'),
+                    cancelResult: false,
                     okTitle: this.translateService.instant('Update'),
+                    okResult: true,
                     confirmAndCancel: true,
                 },
             });
             return dialogRef.afterClosed().pipe(
-                map(shouldUpdate => {
-                    if (shouldUpdate) {
+                switchMap(dialogResult => {
+                    if (dialogResult === true) {
                         
this.dashboard.dashboardGeneralSettings.defaultViewMode =
                             this.viewMode;
-                        this.dashboardService
+                        return this.dashboardService
                             .updateDashboard(this.dashboard)
-                            .subscribe(result => {
-                                return true;
-                            });
+                            .pipe(map(() => true));
                     }
-                    return true;
+
+                    if (dialogResult === false) {
+                        return of(true);
+                    }
+
+                    return of(false);
                 }),
             );
         } else {

Reply via email to