diff --git a/src/app/modules/forms/ix-forms/components/ix-select/ix-select-with-new-option.directive.ts b/src/app/modules/forms/ix-forms/components/ix-select/ix-select-with-new-option.directive.ts index 9857c9b5859..e06a21d5803 100644 --- a/src/app/modules/forms/ix-forms/components/ix-select/ix-select-with-new-option.directive.ts +++ b/src/app/modules/forms/ix-forms/components/ix-select/ix-select-with-new-option.directive.ts @@ -1,6 +1,5 @@ -import { ComponentType } from '@angular/cdk/portal'; import { - AfterViewInit, Directive, Input, OnInit, ViewChild, inject, + AfterViewInit, Directive, Input, OnInit, Type, ViewChild, inject, } from '@angular/core'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService } from '@ngx-translate/core'; @@ -9,6 +8,7 @@ import { Observable, distinctUntilChanged, filter, map, switchMap, take, tap, } from 'rxjs'; import { Option } from 'app/interfaces/option.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { IxSelectComponent, IxSelectValue } from 'app/modules/forms/ix-forms/components/ix-select/ix-select.component'; import { ChainedComponentResponse, ChainedSlideInService } from 'app/services/chained-slide-in.service'; @@ -47,7 +47,7 @@ export abstract class IxSelectWithNewOption implements OnInit, AfterViewInit { abstract getValueFromChainedResponse( result: ChainedComponentResponse, ): IxSelectValue; - abstract getFormComponentType(): ComponentType; + abstract getFormComponentType(): Type; abstract fetchOptions(): Observable; getFormInputData(): Record { return undefined; diff --git a/src/app/modules/slide-ins/chained-component-ref.ts b/src/app/modules/slide-ins/chained-component-ref.ts index fb4dbfb0507..be7af563a57 100644 --- a/src/app/modules/slide-ins/chained-component-ref.ts +++ b/src/app/modules/slide-ins/chained-component-ref.ts @@ -1,4 +1,5 @@ import { Type } from '@angular/core'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { ChainedComponentResponse as ChainedResponse } from 'app/services/chained-slide-in.service'; export class ChainedRef { @@ -10,6 +11,6 @@ export class ChainedRef { * have the same purpose and return the same response type e.g, form to wizard and * wizard to form. */ - swap?: (component: Type, wide: boolean, data?: unknown) => void; + swap?: (component: Type, wide: boolean, data?: unknown) => void; getData: () => T; } diff --git a/src/app/modules/slide-ins/components/modal-header2/modal-header2.component.ts b/src/app/modules/slide-ins/components/modal-header2/modal-header2.component.ts index 5f54252cbab..64bf10277d8 100644 --- a/src/app/modules/slide-ins/components/modal-header2/modal-header2.component.ts +++ b/src/app/modules/slide-ins/components/modal-header2/modal-header2.component.ts @@ -70,6 +70,6 @@ export class ModalHeader2Component implements AfterViewInit { } close(): void { - this.chainedSlideInRef.close({ response: false, error: null }); + this.chainedSlideInRef.close({ response: false, error: null, cancelled: true }); } } diff --git a/src/app/modules/slide-ins/components/slide-in2/slide-in2.component.ts b/src/app/modules/slide-ins/components/slide-in2/slide-in2.component.ts index a10b1bc006f..61666028bbd 100644 --- a/src/app/modules/slide-ins/components/slide-in2/slide-in2.component.ts +++ b/src/app/modules/slide-ins/components/slide-in2/slide-in2.component.ts @@ -118,7 +118,7 @@ export class SlideIn2Component implements OnInit, OnDestroy { } private openSlideIn( - componentType: Type, + componentType: Type, params?: { wide?: boolean; data?: D }, ): void { if (this.isSlideInOpen) { @@ -143,7 +143,7 @@ export class SlideIn2Component implements OnInit, OnDestroy { } private createInjector( - componentType: Type, + componentType: Type, data?: D, ): void { const injector = Injector.create({ @@ -152,7 +152,7 @@ export class SlideIn2Component implements OnInit, OnDestroy { provide: ChainedRef, useValue: { close: (response: ChainedComponentResponse) => { - this.getConfirmation().pipe( + (response.cancelled ? this.getConfirmation() : of(true)).pipe( filter(Boolean), untilDestroyed(this), ).subscribe({ @@ -163,7 +163,7 @@ export class SlideIn2Component implements OnInit, OnDestroy { }, }); }, - swap: (component: Type, wide = false, incomingComponentData?: unknown) => { + swap: (component: Type, wide = false, incomingComponentData?: unknown) => { this.getConfirmation().pipe( filter(Boolean), untilDestroyed(this), @@ -186,7 +186,7 @@ export class SlideIn2Component implements OnInit, OnDestroy { }, ], }); - this.componentRef = this.slideInBody.createComponent(componentType as Type, { injector }); + this.componentRef = this.slideInBody.createComponent(componentType as unknown as Type, { injector }); } protected onBackdropClicked(): void { diff --git a/src/app/pages/dashboard/components/widget-group-form/widget-group-form.component.ts b/src/app/pages/dashboard/components/widget-group-form/widget-group-form.component.ts index b80532c6bb7..bcd8757c6e2 100644 --- a/src/app/pages/dashboard/components/widget-group-form/widget-group-form.component.ts +++ b/src/app/pages/dashboard/components/widget-group-form/widget-group-form.component.ts @@ -7,7 +7,8 @@ import { import { MatButton } from '@angular/material/button'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateModule } from '@ngx-translate/core'; -import { tap } from 'rxjs'; +import { Observable, of, tap } from 'rxjs'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxFieldsetComponent } from 'app/modules/forms/ix-forms/components/ix-fieldset/ix-fieldset.component'; import { IxIconGroupComponent } from 'app/modules/forms/ix-forms/components/ix-icon-group/ix-icon-group.component'; @@ -47,7 +48,7 @@ import { WidgetGroupSlotFormComponent } from './widget-group-slot-form/widget-gr TranslateModule, ], }) -export class WidgetGroupFormComponent { +export class WidgetGroupFormComponent implements SlideIn2CloseConfirmation { protected group = signal( { layout: WidgetGroupLayout.Full, slots: [{ type: null }] }, ); @@ -82,6 +83,10 @@ export class WidgetGroupFormComponent { this.setInitialFormValues(); } + requiresConfirmationOnClose(): Observable { + return of(this.layoutControl.dirty); + } + private setInitialFormValues(): void { const widgetGroup = this.chainedRef.getData(); if (!widgetGroup) { diff --git a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts index 1176c10ab7b..c0f6d140fee 100644 --- a/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts +++ b/src/app/pages/data-protection/cloud-backup/cloud-backup-form/cloud-backup-form.component.ts @@ -8,7 +8,7 @@ import { FormBuilder } from '@ngneat/reactive-forms'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; import { - debounceTime, distinctUntilChanged, map, of, + debounceTime, distinctUntilChanged, map, Observable, of, } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { CloudSyncProviderName } from 'app/enums/cloudsync-provider.enum'; @@ -19,6 +19,7 @@ import { buildNormalizedFileSize } from 'app/helpers/file-size.utils'; import { helptextCloudBackup } from 'app/helptext/data-protection/cloud-backup/cloud-backup'; import { CloudBackup, CloudBackupUpdate } from 'app/interfaces/cloud-backup.interface'; import { SelectOption, newOption } from 'app/interfaces/option.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { ExplorerNodeData, TreeNode } from 'app/interfaces/tree-node.interface'; import { CloudCredentialsSelectComponent } from 'app/modules/forms/custom-selects/cloud-credentials-select/cloud-credentials-select.component'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; @@ -73,7 +74,7 @@ type FormValue = CloudBackupFormComponent['form']['value']; TranslateModule, ], }) -export class CloudBackupFormComponent implements OnInit { +export class CloudBackupFormComponent implements OnInit, SlideIn2CloseConfirmation { get isNew(): boolean { return !this.editingTask; } @@ -143,6 +144,10 @@ export class CloudBackupFormComponent implements OnInit { this.editingTask = chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.setFileNodeProvider(); this.setBucketNodeProvider(); diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts b/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts index 722db9e00a7..e5884961348 100644 --- a/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts +++ b/src/app/pages/data-protection/cloudsync/cloudsync-form/cloudsync-form.component.ts @@ -29,6 +29,7 @@ import { CloudSyncTask, CloudSyncTaskUi, CloudSyncTaskUpdate } from 'app/interfa import { CloudSyncCredential } from 'app/interfaces/cloudsync-credential.interface'; import { CloudSyncProvider } from 'app/interfaces/cloudsync-provider.interface'; import { newOption, SelectOption } from 'app/interfaces/option.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { ExplorerNodeData, TreeNode } from 'app/interfaces/tree-node.interface'; import { WebSocketError } from 'app/interfaces/websocket-error.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; @@ -94,7 +95,7 @@ type FormValue = CloudSyncFormComponent['form']['value']; TranslateModule, ], }) -export class CloudSyncFormComponent implements OnInit { +export class CloudSyncFormComponent implements OnInit, SlideIn2CloseConfirmation { get isNew(): boolean { return !this.editingTask; } @@ -227,6 +228,10 @@ export class CloudSyncFormComponent implements OnInit { this.editingTask = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + getCredentialsList(): Observable { return this.fetchCloudSyncCredentialsList(); } diff --git a/src/app/pages/data-protection/cloudsync/cloudsync-wizard/cloudsync-wizard.component.ts b/src/app/pages/data-protection/cloudsync/cloudsync-wizard/cloudsync-wizard.component.ts index 2ebdfec89a1..64a83f8d6f4 100644 --- a/src/app/pages/data-protection/cloudsync/cloudsync-wizard/cloudsync-wizard.component.ts +++ b/src/app/pages/data-protection/cloudsync/cloudsync-wizard/cloudsync-wizard.component.ts @@ -8,11 +8,13 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; import { BehaviorSubject, Observable, merge, + of, } from 'rxjs'; import { cloudSyncProviderNameMap } from 'app/enums/cloudsync-provider.enum'; import { Role } from 'app/enums/role.enum'; import { CloudSyncTask, CloudSyncTaskUpdate } from 'app/interfaces/cloud-sync-task.interface'; import { CloudSyncCredential } from 'app/interfaces/cloudsync-credential.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { UseIxIconsInStepperComponent, @@ -43,7 +45,7 @@ import { CloudSyncProviderComponent } from './steps/cloudsync-provider/cloudsync UseIxIconsInStepperComponent, ], }) -export class CloudSyncWizardComponent { +export class CloudSyncWizardComponent implements SlideIn2CloseConfirmation { @ViewChild(forwardRef(() => CloudSyncWhatAndWhenComponent)) whatAndWhen: CloudSyncWhatAndWhenComponent; protected readonly requiredRoles = [Role.CloudSyncWrite]; @@ -63,6 +65,10 @@ export class CloudSyncWizardComponent { private errorHandler: ErrorHandlerService, ) {} + requiresConfirmationOnClose(): Observable { + return of(this.whatAndWhen.form.dirty); + } + createTask(payload: CloudSyncTaskUpdate): Observable { return this.ws.call('cloudsync.create', [payload]); } diff --git a/src/app/pages/data-protection/replication/replication-form/replication-form.component.ts b/src/app/pages/data-protection/replication/replication-form/replication-form.component.ts index cdd457e2ccf..20c1cd8fcb5 100644 --- a/src/app/pages/data-protection/replication/replication-form/replication-form.component.ts +++ b/src/app/pages/data-protection/replication/replication-form/replication-form.component.ts @@ -6,7 +6,7 @@ import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { merge, of } from 'rxjs'; +import { merge, Observable, of } from 'rxjs'; import { debounceTime, switchMap } from 'rxjs/operators'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Direction } from 'app/enums/direction.enum'; @@ -17,6 +17,7 @@ import { helptextReplicationWizard } from 'app/helptext/data-protection/replicat import { CountManualSnapshotsParams } from 'app/interfaces/count-manual-snapshots.interface'; import { KeychainSshCredentials } from 'app/interfaces/keychain-credential.interface'; import { ReplicationCreate, ReplicationTask } from 'app/interfaces/replication-task.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { WebSocketError } from 'app/interfaces/websocket-error.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { TreeNodeProvider } from 'app/modules/forms/ix-forms/components/ix-explorer/tree-node-provider.interface'; @@ -74,7 +75,7 @@ import { WebSocketService } from 'app/services/ws.service'; TranslateModule, ], }) -export class ReplicationFormComponent implements OnInit { +export class ReplicationFormComponent implements OnInit, SlideIn2CloseConfirmation { @ViewChild(GeneralSectionComponent, { static: true }) generalSection: GeneralSectionComponent; @ViewChild(TransportSectionComponent, { static: true }) transportSection: TransportSectionComponent; @ViewChild(SourceSectionComponent, { static: true }) sourceSection: SourceSectionComponent; @@ -112,6 +113,16 @@ export class ReplicationFormComponent implements OnInit { this.existingReplication = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of( + this.generalSection.form.dirty + || this.transportSection.form.dirty + || this.sourceSection.form.dirty + || this.targetSection.form.dirty + || this.scheduleSection.form.dirty, + ); + } + ngOnInit(): void { this.countSnapshotsOnChanges(); this.updateExplorersOnChanges(); diff --git a/src/app/pages/data-protection/replication/replication-wizard/replication-wizard.component.ts b/src/app/pages/data-protection/replication/replication-wizard/replication-wizard.component.ts index 5d070e3b476..5b9e36f8a87 100644 --- a/src/app/pages/data-protection/replication/replication-wizard/replication-wizard.component.ts +++ b/src/app/pages/data-protection/replication/replication-wizard/replication-wizard.component.ts @@ -30,6 +30,7 @@ import { CountManualSnapshotsParams, EligibleManualSnapshotsCount, TargetUnmatch import { PeriodicSnapshotTask, PeriodicSnapshotTaskCreate } from 'app/interfaces/periodic-snapshot-task.interface'; import { ReplicationCreate, ReplicationTask } from 'app/interfaces/replication-task.interface'; import { Schedule } from 'app/interfaces/schedule.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { CreateZfsSnapshot, ZfsSnapshot } from 'app/interfaces/zfs-snapshot.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { @@ -69,7 +70,7 @@ import { WebSocketService } from 'app/services/ws.service'; UseIxIconsInStepperComponent, ], }) -export class ReplicationWizardComponent { +export class ReplicationWizardComponent implements SlideIn2CloseConfirmation { @ViewChild(ReplicationWhatAndWhereComponent) whatAndWhere: ReplicationWhatAndWhereComponent; @ViewChild(ReplicationWhenComponent) when: ReplicationWhenComponent; @@ -98,6 +99,11 @@ export class ReplicationWizardComponent { private authService: AuthService, ) {} + requiresConfirmationOnClose(): Observable { + const steps = this.getSteps(); + return of(steps.some((step) => step.form.dirty)); + } + getSteps(): [ ReplicationWhatAndWhereComponent, ReplicationWhenComponent, diff --git a/src/app/pages/data-protection/rsync-task/rsync-task-form/rsync-task-form.component.ts b/src/app/pages/data-protection/rsync-task/rsync-task-form/rsync-task-form.component.ts index 6ddb7ce6e9b..e04c2c0bf7f 100644 --- a/src/app/pages/data-protection/rsync-task/rsync-task-form/rsync-task-form.component.ts +++ b/src/app/pages/data-protection/rsync-task/rsync-task-form/rsync-task-form.component.ts @@ -16,6 +16,7 @@ import { RsyncMode, RsyncSshConnectMode } from 'app/enums/rsync-mode.enum'; import { helptextRsyncForm } from 'app/helptext/data-protection/rsync/rsync-form'; import { newOption } from 'app/interfaces/option.interface'; import { RsyncTask, RsyncTaskUpdate } from 'app/interfaces/rsync-task.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { SshCredentialsSelectComponent } from 'app/modules/forms/custom-selects/ssh-credentials-select/ssh-credentials-select.component'; import { UserComboboxProvider } from 'app/modules/forms/ix-forms/classes/user-combobox-provider'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; @@ -68,7 +69,7 @@ import { WebSocketService } from 'app/services/ws.service'; TranslateModule, ], }) -export class RsyncTaskFormComponent implements OnInit { +export class RsyncTaskFormComponent implements OnInit, SlideIn2CloseConfirmation { readonly requiredRoles = [Role.FullAdmin]; get isNew(): boolean { @@ -154,6 +155,10 @@ export class RsyncTaskFormComponent implements OnInit { this.editingTask = this.chainedSlideInRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + get isModuleMode(): boolean { return this.form.value.mode === RsyncMode.Module; } diff --git a/src/app/pages/system/advanced/access/access-form/access-form.component.ts b/src/app/pages/system/advanced/access/access-form/access-form.component.ts index 6a7a8914ff5..263e583e728 100644 --- a/src/app/pages/system/advanced/access/access-form/access-form.component.ts +++ b/src/app/pages/system/advanced/access/access-form/access-form.component.ts @@ -8,10 +8,11 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; import { - filter, finalize, forkJoin, Observable, take, + filter, finalize, forkJoin, Observable, of, take, } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; @@ -55,7 +56,7 @@ import { selectAdvancedConfig, selectGeneralConfig } from 'app/store/system-conf TranslateModule, ], }) -export class AccessFormComponent implements OnInit { +export class AccessFormComponent implements OnInit, SlideIn2CloseConfirmation { readonly requiredRoles = [Role.AuthSessionsWrite]; isLoading = false; @@ -87,6 +88,10 @@ export class AccessFormComponent implements OnInit { private chainedSlideInRef: ChainedRef, ) {} + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.store$.select(selectPreferences).pipe(untilDestroyed(this)).subscribe((preferences) => { if (preferences.lifetime) { diff --git a/src/app/pages/system/advanced/allowed-addresses/allowed-addresses-form/allowed-addresses-form.component.ts b/src/app/pages/system/advanced/allowed-addresses/allowed-addresses-form/allowed-addresses-form.component.ts index e86c81d6a9c..661cb77b4f1 100644 --- a/src/app/pages/system/advanced/allowed-addresses/allowed-addresses-form/allowed-addresses-form.component.ts +++ b/src/app/pages/system/advanced/allowed-addresses/allowed-addresses-form/allowed-addresses-form.component.ts @@ -15,6 +15,7 @@ import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-r import { Role } from 'app/enums/role.enum'; import { helptextSystemAdvanced } from 'app/helptext/system/advanced'; import { helptextSystemGeneral } from 'app/helptext/system/general'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { WebSocketError } from 'app/interfaces/websocket-error.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; @@ -53,7 +54,7 @@ import { generalConfigUpdated } from 'app/store/system-config/system-config.acti TranslateModule, ], }) -export class AllowedAddressesFormComponent implements OnInit { +export class AllowedAddressesFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; protected readonly helpText = helptextSystemAdvanced; @@ -74,6 +75,10 @@ export class AllowedAddressesFormComponent implements OnInit { private slideInRef: ChainedRef, ) {} + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.ws.call('system.general.config').pipe(untilDestroyed(this)).subscribe({ next: (config) => { diff --git a/src/app/pages/system/advanced/audit/audit-form/audit-form.component.ts b/src/app/pages/system/advanced/audit/audit-form/audit-form.component.ts index a2bd587b91c..ef899441294 100644 --- a/src/app/pages/system/advanced/audit/audit-form/audit-form.component.ts +++ b/src/app/pages/system/advanced/audit/audit-form/audit-form.component.ts @@ -10,6 +10,8 @@ import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; import { EMPTY, + Observable, + of, } from 'rxjs'; import { catchError, tap, @@ -18,6 +20,7 @@ import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-r import { Role } from 'app/enums/role.enum'; import { helptextSystemAdvanced as helptext } from 'app/helptext/system/advanced'; import { AuditConfig } from 'app/interfaces/audit/audit.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxFieldsetComponent } from 'app/modules/forms/ix-forms/components/ix-fieldset/ix-fieldset.component'; @@ -52,7 +55,7 @@ import { advancedConfigUpdated } from 'app/store/system-config/system-config.act TranslateModule, ], }) -export class AuditFormComponent implements OnInit { +export class AuditFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.SystemAuditWrite]; isFormLoading = false; @@ -86,6 +89,10 @@ export class AuditFormComponent implements OnInit { private chainedRef: ChainedRef, ) {} + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.loadForm(); } diff --git a/src/app/pages/system/advanced/console/console-form/console-form.component.ts b/src/app/pages/system/advanced/console/console-form/console-form.component.ts index b51e07df1dc..9d5c0a00dcc 100644 --- a/src/app/pages/system/advanced/console/console-form/console-form.component.ts +++ b/src/app/pages/system/advanced/console/console-form/console-form.component.ts @@ -8,11 +8,12 @@ import { FormBuilder } from '@ngneat/reactive-forms'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { of, Subscription } from 'rxjs'; +import { Observable, of, Subscription } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; import { choicesToOptions } from 'app/helpers/operators/options.operators'; import { helptextSystemAdvanced as helptext } from 'app/helptext/system/advanced'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; import { IxFieldsetComponent } from 'app/modules/forms/ix-forms/components/ix-fieldset/ix-fieldset.component'; @@ -50,7 +51,7 @@ import { advancedConfigUpdated } from 'app/store/system-config/system-config.act TranslateModule, ], }) -export class ConsoleFormComponent implements OnInit { +export class ConsoleFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; isFormLoading = false; @@ -98,6 +99,10 @@ export class ConsoleFormComponent implements OnInit { this.consoleConfig = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.form.patchValue({ consolemenu: this.consoleConfig.consolemenu, diff --git a/src/app/pages/system/advanced/cron/cron-form/cron-form.component.ts b/src/app/pages/system/advanced/cron/cron-form/cron-form.component.ts index f9f9b4006a6..d2883bcf16c 100644 --- a/src/app/pages/system/advanced/cron/cron-form/cron-form.component.ts +++ b/src/app/pages/system/advanced/cron/cron-form/cron-form.component.ts @@ -6,11 +6,12 @@ import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; import { helptextCron } from 'app/helptext/system/cron-form'; import { Cronjob, CronjobUpdate } from 'app/interfaces/cronjob.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { UserComboboxProvider } from 'app/modules/forms/ix-forms/classes/user-combobox-provider'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; @@ -52,7 +53,7 @@ import { WebSocketService } from 'app/services/ws.service'; TranslateModule, ], }) -export class CronFormComponent implements OnInit { +export class CronFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; get isNew(): boolean { @@ -104,6 +105,10 @@ export class CronFormComponent implements OnInit { this.editingCron = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { if (this.editingCron) { this.setCronForEdit(); diff --git a/src/app/pages/system/advanced/global-two-factor-auth/global-two-factor-form/global-two-factor-form.component.ts b/src/app/pages/system/advanced/global-two-factor-auth/global-two-factor-form/global-two-factor-form.component.ts index 5139eeec933..1f79360fe71 100644 --- a/src/app/pages/system/advanced/global-two-factor-auth/global-two-factor-form/global-two-factor-form.component.ts +++ b/src/app/pages/system/advanced/global-two-factor-auth/global-two-factor-form/global-two-factor-form.component.ts @@ -9,11 +9,12 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; import { isEqual } from 'lodash-es'; import { - EMPTY, catchError, filter, of, switchMap, tap, + EMPTY, Observable, catchError, filter, of, switchMap, tap, } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; import { WINDOW } from 'app/helpers/window.helper'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { GlobalTwoFactorConfig, GlobalTwoFactorConfigUpdate } from 'app/interfaces/two-factor-config.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; @@ -49,7 +50,7 @@ import { WebSocketService } from 'app/services/ws.service'; TranslateModule, ], }) -export class GlobalTwoFactorAuthFormComponent implements OnInit { +export class GlobalTwoFactorAuthFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; isFormLoading = false; @@ -79,6 +80,10 @@ export class GlobalTwoFactorAuthFormComponent implements OnInit { this.twoFactorConfig = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.setupForm(); } diff --git a/src/app/pages/system/advanced/init-shutdown/init-shutdown-form/init-shutdown-form.component.ts b/src/app/pages/system/advanced/init-shutdown/init-shutdown-form/init-shutdown-form.component.ts index c6935e58722..1f5a360f644 100644 --- a/src/app/pages/system/advanced/init-shutdown/init-shutdown-form/init-shutdown-form.component.ts +++ b/src/app/pages/system/advanced/init-shutdown/init-shutdown-form/init-shutdown-form.component.ts @@ -16,6 +16,7 @@ import { Role } from 'app/enums/role.enum'; import { mapToOptions } from 'app/helpers/options.helper'; import { helptextInitShutdown } from 'app/helptext/system/init-shutdown'; import { InitShutdownScript } from 'app/interfaces/init-shutdown-script.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; import { IxExplorerComponent } from 'app/modules/forms/ix-forms/components/ix-explorer/ix-explorer.component'; @@ -54,7 +55,7 @@ import { WebSocketService } from 'app/services/ws.service'; AsyncPipe, ], }) -export class InitShutdownFormComponent implements OnInit { +export class InitShutdownFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; get isNew(): boolean { @@ -113,6 +114,10 @@ export class InitShutdownFormComponent implements OnInit { this.editingScript = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.subscriptions.push( this.form.controls.command.enabledWhile(this.isCommand$), diff --git a/src/app/pages/system/advanced/isolated-gpus/isolated-gpus-form/isolated-gpus-form.component.ts b/src/app/pages/system/advanced/isolated-gpus/isolated-gpus-form/isolated-gpus-form.component.ts index 63ee447b0c4..9e107be821d 100644 --- a/src/app/pages/system/advanced/isolated-gpus/isolated-gpus-form/isolated-gpus-form.component.ts +++ b/src/app/pages/system/advanced/isolated-gpus/isolated-gpus-form/isolated-gpus-form.component.ts @@ -7,9 +7,12 @@ import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { map, take } from 'rxjs'; +import { + map, Observable, of, take, +} from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxFieldsetComponent } from 'app/modules/forms/ix-forms/components/ix-fieldset/ix-fieldset.component'; import { IxSelectComponent } from 'app/modules/forms/ix-forms/components/ix-select/ix-select.component'; @@ -44,7 +47,7 @@ import { waitForAdvancedConfig } from 'app/store/system-config/system-config.sel TranslateModule, ], }) -export class IsolatedGpusFormComponent implements OnInit { +export class IsolatedGpusFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; isFormLoading = false; @@ -72,6 +75,10 @@ export class IsolatedGpusFormComponent implements OnInit { private chainedRef: ChainedRef, ) { } + requiresConfirmationOnClose(): Observable { + return of(this.formGroup.dirty); + } + ngOnInit(): void { this.store$.pipe( waitForAdvancedConfig, diff --git a/src/app/pages/system/advanced/kernel/kernel-form/kernel-form.component.ts b/src/app/pages/system/advanced/kernel/kernel-form/kernel-form.component.ts index e58c1633f7a..7ac881ec5d8 100644 --- a/src/app/pages/system/advanced/kernel/kernel-form/kernel-form.component.ts +++ b/src/app/pages/system/advanced/kernel/kernel-form/kernel-form.component.ts @@ -7,9 +7,11 @@ import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; +import { Observable, of } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; import { helptextSystemAdvanced } from 'app/helptext/system/advanced'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; @@ -43,7 +45,7 @@ import { advancedConfigUpdated } from 'app/store/system-config/system-config.act TranslateModule, ], }) -export class KernelFormComponent implements OnInit { +export class KernelFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; isFormLoading = false; @@ -73,6 +75,10 @@ export class KernelFormComponent implements OnInit { } } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.setupForm(); } diff --git a/src/app/pages/system/advanced/replication/replication-settings-form/replication-settings-form.component.ts b/src/app/pages/system/advanced/replication/replication-settings-form/replication-settings-form.component.ts index 866e65f0ec7..6085172e130 100644 --- a/src/app/pages/system/advanced/replication/replication-settings-form/replication-settings-form.component.ts +++ b/src/app/pages/system/advanced/replication/replication-settings-form/replication-settings-form.component.ts @@ -6,10 +6,12 @@ import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; +import { Observable, of } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; import { helptextSystemAdvanced } from 'app/helptext/system/advanced'; import { ReplicationConfig } from 'app/interfaces/replication-config.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxFieldsetComponent } from 'app/modules/forms/ix-forms/components/ix-fieldset/ix-fieldset.component'; @@ -41,7 +43,7 @@ import { WebSocketService } from 'app/services/ws.service'; TranslateModule, ], }) -export class ReplicationSettingsFormComponent implements OnInit { +export class ReplicationSettingsFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.ReplicationTaskConfigWrite]; isFormLoading = false; @@ -68,6 +70,10 @@ export class ReplicationSettingsFormComponent implements OnInit { this.replicationConfig = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.form.patchValue(this.replicationConfig); } diff --git a/src/app/pages/system/advanced/self-encrypting-drive/self-encrypting-drive-form/self-encrypting-drive-form.component.ts b/src/app/pages/system/advanced/self-encrypting-drive/self-encrypting-drive-form/self-encrypting-drive-form.component.ts index b8d5d7ac543..7708659c534 100644 --- a/src/app/pages/system/advanced/self-encrypting-drive/self-encrypting-drive-form/self-encrypting-drive-form.component.ts +++ b/src/app/pages/system/advanced/self-encrypting-drive/self-encrypting-drive-form/self-encrypting-drive-form.component.ts @@ -7,11 +7,12 @@ import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { of } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; import { SedUser } from 'app/enums/sed-user.enum'; import { helptextSystemAdvanced } from 'app/helptext/system/advanced'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { IxFieldsetComponent } from 'app/modules/forms/ix-forms/components/ix-fieldset/ix-fieldset.component'; import { IxInputComponent } from 'app/modules/forms/ix-forms/components/ix-input/ix-input.component'; @@ -52,7 +53,7 @@ export interface SedConfig { TranslateModule, ], }) -export class SelfEncryptingDriveFormComponent implements OnInit { +export class SelfEncryptingDriveFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; isFormLoading = false; @@ -103,6 +104,10 @@ export class SelfEncryptingDriveFormComponent implements OnInit { this.sedConfig = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.loadConfig(); } diff --git a/src/app/pages/system/advanced/storage/storage-settings-form/storage-settings-form.component.ts b/src/app/pages/system/advanced/storage/storage-settings-form/storage-settings-form.component.ts index 067900a922a..c82ac6747a9 100644 --- a/src/app/pages/system/advanced/storage/storage-settings-form/storage-settings-form.component.ts +++ b/src/app/pages/system/advanced/storage/storage-settings-form/storage-settings-form.component.ts @@ -18,6 +18,7 @@ import { Role } from 'app/enums/role.enum'; import { ServiceName } from 'app/enums/service-name.enum'; import { ServiceStatus } from 'app/enums/service-status.enum'; import { choicesToOptions } from 'app/helpers/operators/options.operators'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxFieldsetComponent } from 'app/modules/forms/ix-forms/components/ix-fieldset/ix-fieldset.component'; @@ -58,7 +59,7 @@ export interface StorageSettings { AsyncPipe, ], }) -export class StorageSettingsFormComponent implements OnInit { +export class StorageSettingsFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; isFormLoading = false; @@ -86,6 +87,10 @@ export class StorageSettingsFormComponent implements OnInit { this.storageSettings = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.loadFormData(); } diff --git a/src/app/pages/system/advanced/sysctl/tunable-form/tunable-form.component.ts b/src/app/pages/system/advanced/sysctl/tunable-form/tunable-form.component.ts index 7ec82fbbf8f..ad6333ac378 100644 --- a/src/app/pages/system/advanced/sysctl/tunable-form/tunable-form.component.ts +++ b/src/app/pages/system/advanced/sysctl/tunable-form/tunable-form.component.ts @@ -6,12 +6,13 @@ import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { Observable } from 'rxjs'; +import { Observable, of } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; import { TunableType } from 'app/enums/tunable-type.enum'; import { helptextSystemTunable as helptext } from 'app/helptext/system/tunable'; import { Job } from 'app/interfaces/job.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { Tunable } from 'app/interfaces/tunable.interface'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; @@ -46,7 +47,7 @@ import { WebSocketService } from 'app/services/ws.service'; TranslateModule, ], }) -export class TunableFormComponent implements OnInit { +export class TunableFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; get isNew(): boolean { @@ -86,6 +87,10 @@ export class TunableFormComponent implements OnInit { this.editingTunable = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { if (this.editingTunable) { this.setTunableForEdit(); diff --git a/src/app/pages/system/advanced/syslog/syslog-form/syslog-form.component.ts b/src/app/pages/system/advanced/syslog/syslog-form/syslog-form.component.ts index 9ce34f9ccc2..b2ddc1b21ad 100644 --- a/src/app/pages/system/advanced/syslog/syslog-form/syslog-form.component.ts +++ b/src/app/pages/system/advanced/syslog/syslog-form/syslog-form.component.ts @@ -10,7 +10,7 @@ import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; import { - EMPTY, of, Subscription, + EMPTY, Observable, of, Subscription, } from 'rxjs'; import { catchError, tap, @@ -21,6 +21,7 @@ import { SyslogLevel, SyslogTransport } from 'app/enums/syslog.enum'; import { choicesToOptions } from 'app/helpers/operators/options.operators'; import { helptextSystemAdvanced, helptextSystemAdvanced as helptext } from 'app/helptext/system/advanced'; import { AdvancedConfigUpdate } from 'app/interfaces/advanced-config.interface'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; import { IxCheckboxComponent } from 'app/modules/forms/ix-forms/components/ix-checkbox/ix-checkbox.component'; import { IxFieldsetComponent } from 'app/modules/forms/ix-forms/components/ix-fieldset/ix-fieldset.component'; @@ -59,7 +60,7 @@ import { advancedConfigUpdated } from 'app/store/system-config/system-config.act AsyncPipe, ], }) -export class SyslogFormComponent implements OnInit { +export class SyslogFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; isFormLoading = false; @@ -111,6 +112,10 @@ export class SyslogFormComponent implements OnInit { this.syslogConfig = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { this.subscriptions.push( this.form.controls.syslog_tls_certificate.enabledWhile(this.isTlsTransport$), diff --git a/src/app/pages/system/advanced/system-security/system-security-form/system-security-form.component.ts b/src/app/pages/system/advanced/system-security/system-security-form/system-security-form.component.ts index 492279c7017..f359dbcaa89 100644 --- a/src/app/pages/system/advanced/system-security/system-security-form/system-security-form.component.ts +++ b/src/app/pages/system/advanced/system-security/system-security-form/system-security-form.component.ts @@ -7,10 +7,11 @@ import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { Store } from '@ngrx/store'; import { TranslateService, TranslateModule } from '@ngx-translate/core'; -import { EMPTY, Observable } from 'rxjs'; +import { EMPTY, Observable, of } from 'rxjs'; import { switchMap, take } from 'rxjs/operators'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { SystemSecurityConfig } from 'app/interfaces/system-security-config.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { IxSlideToggleComponent } from 'app/modules/forms/ix-forms/components/ix-slide-toggle/ix-slide-toggle.component'; @@ -43,7 +44,7 @@ import { selectIsHaLicensed } from 'app/store/ha-info/ha-info.selectors'; TranslateModule, ], }) -export class SystemSecurityFormComponent implements OnInit { +export class SystemSecurityFormComponent implements OnInit, SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.FullAdmin]; form = this.formBuilder.group({ @@ -69,6 +70,10 @@ export class SystemSecurityFormComponent implements OnInit { this.systemSecurityConfig = this.chainedRef.getData(); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + ngOnInit(): void { if (this.systemSecurityConfig) { this.initSystemSecurityForm(); diff --git a/src/app/pages/virtualization/components/all-instances/all-instances-header/global-config-form/global-config-form.component.ts b/src/app/pages/virtualization/components/all-instances/all-instances-header/global-config-form/global-config-form.component.ts index 4a1fc6705f2..f2cdf0cec10 100644 --- a/src/app/pages/virtualization/components/all-instances/all-instances-header/global-config-form/global-config-form.component.ts +++ b/src/app/pages/virtualization/components/all-instances/all-instances-header/global-config-form/global-config-form.component.ts @@ -4,10 +4,11 @@ import { MatButton } from '@angular/material/button'; import { MatCard, MatCardContent } from '@angular/material/card'; import { UntilDestroy, untilDestroyed } from '@ngneat/until-destroy'; import { TranslateModule, TranslateService } from '@ngx-translate/core'; -import { finalize } from 'rxjs'; +import { finalize, Observable, of } from 'rxjs'; import { RequiresRolesDirective } from 'app/directives/requires-roles/requires-roles.directive'; import { Role } from 'app/enums/role.enum'; import { choicesToOptions } from 'app/helpers/operators/options.operators'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { VirtualizationGlobalConfig, VirtualizationGlobalConfigUpdate } from 'app/interfaces/virtualization.interface'; import { DialogService } from 'app/modules/dialog/dialog.service'; import { FormActionsComponent } from 'app/modules/forms/ix-forms/components/form-actions/form-actions.component'; @@ -46,7 +47,7 @@ import { WebSocketService } from 'app/services/ws.service'; IxIpInputWithNetmaskComponent, ], }) -export class GlobalConfigFormComponent { +export class GlobalConfigFormComponent implements SlideIn2CloseConfirmation { protected readonly requiredRoles = [Role.VirtGlobalWrite]; protected isLoading = signal(false); protected readonly autoBridge = '[AUTO]'; @@ -84,6 +85,10 @@ export class GlobalConfigFormComponent { }); } + requiresConfirmationOnClose(): Observable { + return of(this.form.dirty); + } + onSubmit(): void { this.isLoading.set(true); diff --git a/src/app/services/chained-slide-in.service.ts b/src/app/services/chained-slide-in.service.ts index fe3ad6998c7..28ad2a89cb8 100644 --- a/src/app/services/chained-slide-in.service.ts +++ b/src/app/services/chained-slide-in.service.ts @@ -5,10 +5,11 @@ import { UUID } from 'angular2-uuid'; import { Observable, Subject, take, tap, timer, } from 'rxjs'; +import { SlideIn2CloseConfirmation } from 'app/interfaces/slide-in-close-confirmation.interface'; import { FocusService } from 'app/services/focus.service'; export interface IncomingChainedComponent { - component: Type; + component: Type; wide: boolean; data: unknown; swapComponentId?: string; @@ -19,7 +20,7 @@ export interface ChainedSlideInState { } export interface ChainedComponent { - component: Type; + component: Type; close$: Subject; wide: boolean; data: unknown; @@ -29,11 +30,12 @@ export interface ChainedComponent { export interface ChainedComponentResponse { response: T; error: unknown; + cancelled?: boolean; } export interface ChainedComponentSerialized { id: string; - component: Type; + component: Type; close$: Subject; data?: unknown; wide?: boolean; @@ -83,7 +85,7 @@ export class ChainedSlideInService extends ComponentStore { // TODO: Update second argument to options open( - component: Type, + component: Type, wide = false, data?: unknown, ): Observable {