diff --git a/apps/web/src/app/billing/payment/components/change-payment-method-dialog.component.ts b/apps/web/src/app/billing/payment/components/change-payment-method-dialog.component.ts index ff5156ba636..15c63d8f99f 100644 --- a/apps/web/src/app/billing/payment/components/change-payment-method-dialog.component.ts +++ b/apps/web/src/app/billing/payment/components/change-payment-method-dialog.component.ts @@ -1,5 +1,5 @@ import { DIALOG_DATA } from "@angular/cdk/dialog"; -import { Component, Inject, ViewChild } from "@angular/core"; +import { Component, Inject } from "@angular/core"; import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; import { DialogConfig, DialogRef, DialogService, ToastService } from "@bitwarden/components"; @@ -7,19 +7,17 @@ import { DialogConfig, DialogRef, DialogService, ToastService } from "@bitwarden import { SharedModule } from "../../../shared"; import { BillingClient } from "../../services"; import { BillableEntity } from "../../types"; -import { MaskedPaymentMethod } from "../types"; import { EnterPaymentMethodComponent } from "./enter-payment-method.component"; +import { + SubmitPaymentMethodDialogComponent, + SubmitPaymentMethodDialogResult, +} from "./submit-payment-method-dialog.component"; type DialogParams = { owner: BillableEntity; }; -type DialogResult = - | { type: "cancelled" } - | { type: "error" } - | { type: "success"; paymentMethod: MaskedPaymentMethod }; - @Component({ template: `
@@ -55,63 +53,23 @@ type DialogResult = imports: [EnterPaymentMethodComponent, SharedModule], providers: [BillingClient], }) -export class ChangePaymentMethodDialogComponent { - @ViewChild(EnterPaymentMethodComponent) - private enterPaymentMethodComponent!: EnterPaymentMethodComponent; - protected formGroup = EnterPaymentMethodComponent.getFormGroup(); +export class ChangePaymentMethodDialogComponent extends SubmitPaymentMethodDialogComponent { + protected override owner: BillableEntity; constructor( - private billingClient: BillingClient, + billingClient: BillingClient, @Inject(DIALOG_DATA) protected dialogParams: DialogParams, - private dialogRef: DialogRef, - private i18nService: I18nService, - private toastService: ToastService, - ) {} - - submit = async () => { - this.formGroup.markAllAsTouched(); - - if (!this.formGroup.valid) { - return; - } - - const paymentMethod = await this.enterPaymentMethodComponent.tokenize(); - const billingAddress = - this.formGroup.value.type !== "payPal" - ? this.formGroup.controls.billingAddress.getRawValue() - : null; - - const result = await this.billingClient.updatePaymentMethod( - this.dialogParams.owner, - paymentMethod, - billingAddress, - ); - - switch (result.type) { - case "success": { - this.toastService.showToast({ - variant: "success", - title: "", - message: this.i18nService.t("paymentMethodUpdated"), - }); - this.dialogRef.close({ - type: "success", - paymentMethod: result.value, - }); - break; - } - case "error": { - this.toastService.showToast({ - variant: "error", - title: "", - message: result.message, - }); - this.dialogRef.close({ type: "error" }); - break; - } - } - }; + dialogRef: DialogRef, + i18nService: I18nService, + toastService: ToastService, + ) { + super(billingClient, dialogRef, i18nService, toastService); + this.owner = this.dialogParams.owner; + } static open = (dialogService: DialogService, dialogConfig: DialogConfig) => - dialogService.open(ChangePaymentMethodDialogComponent, dialogConfig); + dialogService.open( + ChangePaymentMethodDialogComponent, + dialogConfig, + ); } diff --git a/apps/web/src/app/billing/payment/components/index.ts b/apps/web/src/app/billing/payment/components/index.ts index 3bf7f5ecd36..7e500d2119e 100644 --- a/apps/web/src/app/billing/payment/components/index.ts +++ b/apps/web/src/app/billing/payment/components/index.ts @@ -6,4 +6,6 @@ export * from "./display-payment-method.component"; export * from "./edit-billing-address-dialog.component"; export * from "./enter-billing-address.component"; export * from "./enter-payment-method.component"; +export * from "./require-payment-method-dialog.component"; +export * from "./submit-payment-method-dialog.component"; export * from "./verify-bank-account.component"; diff --git a/apps/web/src/app/billing/payment/components/require-payment-method-dialog.component.ts b/apps/web/src/app/billing/payment/components/require-payment-method-dialog.component.ts new file mode 100644 index 00000000000..72585badca0 --- /dev/null +++ b/apps/web/src/app/billing/payment/components/require-payment-method-dialog.component.ts @@ -0,0 +1,77 @@ +import { DIALOG_DATA } from "@angular/cdk/dialog"; +import { Component, Inject } from "@angular/core"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { + CalloutTypes, + DialogConfig, + DialogRef, + DialogService, + ToastService, +} from "@bitwarden/components"; + +import { SharedModule } from "../../../shared"; +import { BillingClient } from "../../services"; +import { BillableEntity } from "../../types"; + +import { EnterPaymentMethodComponent } from "./enter-payment-method.component"; +import { + SubmitPaymentMethodDialogComponent, + SubmitPaymentMethodDialogResult, +} from "./submit-payment-method-dialog.component"; + +type DialogParams = { + owner: BillableEntity; + callout: { + type: CalloutTypes; + title: string; + message: string; + }; +}; + +@Component({ + template: ` + + + + {{ "addPaymentMethod" | i18n }} + +
+ + {{ dialogParams.callout.message }} + + + +
+ + + +
+ + `, + standalone: true, + imports: [EnterPaymentMethodComponent, SharedModule], + providers: [BillingClient], +}) +export class RequirePaymentMethodDialogComponent extends SubmitPaymentMethodDialogComponent { + protected override owner: BillableEntity; + + constructor( + billingClient: BillingClient, + @Inject(DIALOG_DATA) protected dialogParams: DialogParams, + dialogRef: DialogRef, + i18nService: I18nService, + toastService: ToastService, + ) { + super(billingClient, dialogRef, i18nService, toastService); + this.owner = this.dialogParams.owner; + } + + static open = (dialogService: DialogService, dialogConfig: DialogConfig) => + dialogService.open(RequirePaymentMethodDialogComponent, { + ...dialogConfig, + disableClose: true, + }); +} diff --git a/apps/web/src/app/billing/payment/components/submit-payment-method-dialog.component.ts b/apps/web/src/app/billing/payment/components/submit-payment-method-dialog.component.ts new file mode 100644 index 00000000000..0a0a5bf26d9 --- /dev/null +++ b/apps/web/src/app/billing/payment/components/submit-payment-method-dialog.component.ts @@ -0,0 +1,75 @@ +import { Component, ViewChild } from "@angular/core"; + +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { DialogRef, ToastService } from "@bitwarden/components"; + +import { BillingClient } from "../../services"; +import { BillableEntity } from "../../types"; +import { MaskedPaymentMethod } from "../types"; + +import { EnterPaymentMethodComponent } from "./enter-payment-method.component"; + +export type SubmitPaymentMethodDialogResult = + | { type: "cancelled" } + | { type: "error" } + | { type: "success"; paymentMethod: MaskedPaymentMethod }; + +@Component({ template: "" }) +export abstract class SubmitPaymentMethodDialogComponent { + @ViewChild(EnterPaymentMethodComponent) + private enterPaymentMethodComponent!: EnterPaymentMethodComponent; + protected formGroup = EnterPaymentMethodComponent.getFormGroup(); + + protected abstract owner: BillableEntity; + + protected constructor( + protected billingClient: BillingClient, + protected dialogRef: DialogRef, + protected i18nService: I18nService, + protected toastService: ToastService, + ) {} + + submit = async () => { + this.formGroup.markAllAsTouched(); + + if (!this.formGroup.valid) { + return; + } + + const paymentMethod = await this.enterPaymentMethodComponent.tokenize(); + const billingAddress = + this.formGroup.value.type !== "payPal" + ? this.formGroup.controls.billingAddress.getRawValue() + : null; + + const result = await this.billingClient.updatePaymentMethod( + this.owner, + paymentMethod, + billingAddress, + ); + + switch (result.type) { + case "success": { + this.toastService.showToast({ + variant: "success", + title: "", + message: this.i18nService.t("paymentMethodUpdated"), + }); + this.dialogRef.close({ + type: "success", + paymentMethod: result.value, + }); + break; + } + case "error": { + this.toastService.showToast({ + variant: "error", + title: "", + message: result.message, + }); + this.dialogRef.close({ type: "error" }); + break; + } + } + }; +} diff --git a/apps/web/src/locales/en/messages.json b/apps/web/src/locales/en/messages.json index f34d6e41b37..88a26c6f594 100644 --- a/apps/web/src/locales/en/messages.json +++ b/apps/web/src/locales/en/messages.json @@ -10922,6 +10922,36 @@ } } }, + "unpaidInvoices": { + "message": "Unpaid invoices" + }, + "unpaidInvoicesForServiceUser": { + "message": "Your subscription has not been paid. Contact your provider administrator to restore service to you and your clients.", + "description": "A message shown in a non-dismissible dialog to service users of unpaid providers." + }, + "providerSuspended": { + "message": "$PROVIDER$ is suspended", + "placeholders": { + "provider": { + "content": "$1", + "example": "Acme Industries" + } + } + }, + "restoreProviderPortalAccessViaCustomerSupport": { + "message": "To restore access to your provider portal, contact Bitwarden Customer Support to renew your subscription.", + "description": "A message shown in a non-dismissible dialog to any user of a suspended providers." + }, + "restoreProviderPortalAccessViaPaymentMethod": { + "message": "Your subscription has not been paid. To restore service to you and your clients, add a payment method by $CANCELLATION_DATE$.", + "placeholders": { + "cancellation_date": { + "content": "$1", + "example": "07/10/2025" + } + }, + "description": "A message shown in a non-dismissible dialog to admins of unpaid providers." + }, "subscribetoEnterprise": { "message": "Subscribe to $PLAN$", "placeholders": { diff --git a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts index d6f9af9805d..245c6180eaa 100644 --- a/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts +++ b/bitwarden_license/bit-web/src/app/admin-console/providers/providers-layout.component.ts @@ -17,10 +17,13 @@ import { BusinessUnitPortalLogo } from "@bitwarden/web-vault/app/admin-console/i import { ProviderPortalLogo } from "@bitwarden/web-vault/app/admin-console/icons/provider-portal-logo"; import { WebLayoutModule } from "@bitwarden/web-vault/app/layouts/web-layout.module"; +import { ProviderWarningsService } from "../../billing/providers/services/provider-warnings.service"; + @Component({ selector: "providers-layout", templateUrl: "providers-layout.component.html", imports: [CommonModule, RouterModule, JslibModule, WebLayoutModule, IconModule], + providers: [ProviderWarningsService], }) export class ProvidersLayoutComponent implements OnInit, OnDestroy { protected readonly logo = ProviderPortalLogo; @@ -40,13 +43,18 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy { private route: ActivatedRoute, private providerService: ProviderService, private configService: ConfigService, + private providerWarningsService: ProviderWarningsService, ) {} ngOnInit() { document.body.classList.remove("layout_frontend"); - this.provider$ = this.route.params.pipe( - switchMap((params) => this.providerService.get$(params.providerId)), + const providerId$: Observable = this.route.params.pipe( + map((params) => params.providerId), + ); + + this.provider$ = providerId$.pipe( + switchMap((providerId) => this.providerService.get$(providerId)), takeUntil(this.destroy$), ); @@ -77,6 +85,15 @@ export class ProvidersLayoutComponent implements OnInit, OnDestroy { this.managePaymentDetailsOutsideCheckout$ = this.configService.getFeatureFlag$( FeatureFlag.PM21881_ManagePaymentDetailsOutsideCheckout, ); + + providerId$ + .pipe( + switchMap((providerId) => + this.providerWarningsService.showProviderSuspendedDialog$(providerId), + ), + takeUntil(this.destroy$), + ) + .subscribe(); } ngOnDestroy() { diff --git a/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.spec.ts b/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.spec.ts new file mode 100644 index 00000000000..752df86ed7e --- /dev/null +++ b/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.spec.ts @@ -0,0 +1,187 @@ +import { TestBed } from "@angular/core/testing"; +import { ActivatedRoute, Router } from "@angular/router"; +import { mock, MockProxy } from "jest-mock-extended"; +import { of } from "rxjs"; + +import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; +import { ProviderUserType } from "@bitwarden/common/admin-console/enums"; +import { Provider } from "@bitwarden/common/admin-console/models/domain/provider"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; +import { ProviderSubscriptionResponse } from "@bitwarden/common/billing/models/response/provider-subscription-response"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { SyncService } from "@bitwarden/common/platform/sync"; +import { DialogRef, DialogService } from "@bitwarden/components"; +import { + RequirePaymentMethodDialogComponent, + SubmitPaymentMethodDialogResult, +} from "@bitwarden/web-vault/app/billing/payment/components"; + +import { ProviderWarningsService } from "./provider-warnings.service"; + +describe("ProviderWarningsService", () => { + let service: ProviderWarningsService; + let configService: MockProxy; + let dialogService: MockProxy; + let providerService: MockProxy; + let billingApiService: MockProxy; + let i18nService: MockProxy; + let router: MockProxy; + let syncService: MockProxy; + + beforeEach(() => { + billingApiService = mock(); + configService = mock(); + dialogService = mock(); + i18nService = mock(); + providerService = mock(); + router = mock(); + syncService = mock(); + + TestBed.configureTestingModule({ + providers: [ + ProviderWarningsService, + { provide: ActivatedRoute, useValue: {} }, + { provide: BillingApiServiceAbstraction, useValue: billingApiService }, + { provide: ConfigService, useValue: configService }, + { provide: DialogService, useValue: dialogService }, + { provide: I18nService, useValue: i18nService }, + { provide: ProviderService, useValue: providerService }, + { provide: Router, useValue: router }, + { provide: SyncService, useValue: syncService }, + ], + }); + + service = TestBed.inject(ProviderWarningsService); + }); + + it("should create the service", () => { + expect(service).toBeTruthy(); + }); + + describe("showProviderSuspendedDialog$", () => { + const providerId = "test-provider-id"; + + it("should not show any dialog when the 'pm-21821-provider-portal-takeover' flag is disabled", (done) => { + const provider = { enabled: false } as Provider; + const subscription = { status: "unpaid" } as ProviderSubscriptionResponse; + + providerService.get$.mockReturnValue(of(provider)); + billingApiService.getProviderSubscription.mockResolvedValue(subscription); + configService.getFeatureFlag$.mockReturnValue(of(false)); + + const requirePaymentMethodDialogComponentOpenSpy = jest.spyOn( + RequirePaymentMethodDialogComponent, + "open", + ); + + service.showProviderSuspendedDialog$(providerId).subscribe(() => { + expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); + expect(requirePaymentMethodDialogComponentOpenSpy).not.toHaveBeenCalled(); + done(); + }); + }); + + it("should not show any dialog when the provider is enabled", (done) => { + const provider = { enabled: true } as Provider; + const subscription = { status: "unpaid" } as ProviderSubscriptionResponse; + + providerService.get$.mockReturnValue(of(provider)); + billingApiService.getProviderSubscription.mockResolvedValue(subscription); + configService.getFeatureFlag$.mockReturnValue(of(true)); + + const requirePaymentMethodDialogComponentOpenSpy = jest.spyOn( + RequirePaymentMethodDialogComponent, + "open", + ); + + service.showProviderSuspendedDialog$(providerId).subscribe(() => { + expect(dialogService.openSimpleDialog).not.toHaveBeenCalled(); + expect(requirePaymentMethodDialogComponentOpenSpy).not.toHaveBeenCalled(); + done(); + }); + }); + + it("should show the require payment method dialog for an admin of a provider with an unpaid subscription", (done) => { + const provider = { + enabled: false, + type: ProviderUserType.ProviderAdmin, + name: "Test Provider", + } as Provider; + const subscription = { + status: "unpaid", + cancelAt: "2024-12-31", + } as ProviderSubscriptionResponse; + + providerService.get$.mockReturnValue(of(provider)); + billingApiService.getProviderSubscription.mockResolvedValue(subscription); + configService.getFeatureFlag$.mockReturnValue(of(true)); + + const dialogRef = { + closed: of({ type: "success" }), + } as DialogRef; + jest.spyOn(RequirePaymentMethodDialogComponent, "open").mockReturnValue(dialogRef); + + service.showProviderSuspendedDialog$(providerId).subscribe(() => { + expect(RequirePaymentMethodDialogComponent.open).toHaveBeenCalled(); + expect(syncService.fullSync).toHaveBeenCalled(); + expect(router.navigate).toHaveBeenCalled(); + done(); + }); + }); + + it("should show the simple, unpaid invoices dialog for a service user of a provider with an unpaid subscription", (done) => { + const provider = { + enabled: false, + type: ProviderUserType.ServiceUser, + name: "Test Provider", + } as Provider; + const subscription = { status: "unpaid" } as ProviderSubscriptionResponse; + + providerService.get$.mockReturnValue(of(provider)); + billingApiService.getProviderSubscription.mockResolvedValue(subscription); + dialogService.openSimpleDialog.mockResolvedValue(true); + configService.getFeatureFlag$.mockReturnValue(of(true)); + + i18nService.t.mockImplementation((key: string) => key); + + service.showProviderSuspendedDialog$(providerId).subscribe(() => { + expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({ + type: "danger", + title: "unpaidInvoices", + content: "unpaidInvoicesForServiceUser", + disableClose: true, + }); + done(); + }); + }); + + it("should show the provider suspended dialog to all users of a provider that's suspended, but not unpaid", (done) => { + const provider = { + enabled: false, + name: "Test Provider", + } as Provider; + const subscription = { status: "active" } as ProviderSubscriptionResponse; + + providerService.get$.mockReturnValue(of(provider)); + billingApiService.getProviderSubscription.mockResolvedValue(subscription); + dialogService.openSimpleDialog.mockResolvedValue(true); + configService.getFeatureFlag$.mockReturnValue(of(true)); + + i18nService.t.mockImplementation((key: string) => key); + + service.showProviderSuspendedDialog$(providerId).subscribe(() => { + expect(dialogService.openSimpleDialog).toHaveBeenCalledWith({ + type: "danger", + title: "providerSuspended", + content: "restoreProviderPortalAccessViaCustomerSupport", + disableClose: true, + acceptButtonText: "contactSupportShort", + cancelButtonText: null, + acceptAction: expect.any(Function), + }); + done(); + }); + }); + }); +}); diff --git a/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.ts b/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.ts new file mode 100644 index 00000000000..d888cc6b8d9 --- /dev/null +++ b/bitwarden_license/bit-web/src/app/billing/providers/services/provider-warnings.service.ts @@ -0,0 +1,104 @@ +import { Injectable } from "@angular/core"; +import { ActivatedRoute, Router } from "@angular/router"; +import { combineLatest, from, lastValueFrom, Observable, switchMap } from "rxjs"; + +import { ProviderService } from "@bitwarden/common/admin-console/abstractions/provider.service"; +import { ProviderUserType } from "@bitwarden/common/admin-console/enums"; +import { BillingApiServiceAbstraction } from "@bitwarden/common/billing/abstractions"; +import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum"; +import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service"; +import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service"; +import { SyncService } from "@bitwarden/common/platform/sync"; +import { DialogService } from "@bitwarden/components"; +import { RequirePaymentMethodDialogComponent } from "@bitwarden/web-vault/app/billing/payment/components"; + +@Injectable() +export class ProviderWarningsService { + constructor( + private activatedRoute: ActivatedRoute, + private billingApiService: BillingApiServiceAbstraction, + private configService: ConfigService, + private dialogService: DialogService, + private i18nService: I18nService, + private providerService: ProviderService, + private router: Router, + private syncService: SyncService, + ) {} + + showProviderSuspendedDialog$ = (providerId: string): Observable => + combineLatest([ + this.configService.getFeatureFlag$(FeatureFlag.PM21821_ProviderPortalTakeover), + this.providerService.get$(providerId), + from(this.billingApiService.getProviderSubscription(providerId)), + ]).pipe( + switchMap(async ([providerPortalTakeover, provider, subscription]) => { + if (!providerPortalTakeover || provider.enabled) { + return; + } + + if (subscription.status === "unpaid") { + switch (provider.type) { + case ProviderUserType.ProviderAdmin: { + const cancelAt = subscription.cancelAt + ? new Date(subscription.cancelAt).toLocaleDateString("en-US", { + month: "short", + day: "2-digit", + year: "numeric", + }) + : null; + + const dialogRef = RequirePaymentMethodDialogComponent.open(this.dialogService, { + data: { + owner: { + type: "provider", + data: provider, + }, + callout: { + type: "danger", + title: this.i18nService.t("unpaidInvoices"), + message: this.i18nService.t( + "restoreProviderPortalAccessViaPaymentMethod", + cancelAt ?? undefined, + ), + }, + }, + }); + + const result = await lastValueFrom(dialogRef.closed); + + if (result?.type === "success") { + await this.syncService.fullSync(true); + await this.router.navigate(["."], { + relativeTo: this.activatedRoute, + onSameUrlNavigation: "reload", + }); + } + break; + } + case ProviderUserType.ServiceUser: { + await this.dialogService.openSimpleDialog({ + type: "danger", + title: this.i18nService.t("unpaidInvoices"), + content: this.i18nService.t("unpaidInvoicesForServiceUser"), + disableClose: true, + }); + break; + } + } + } else { + await this.dialogService.openSimpleDialog({ + type: "danger", + title: this.i18nService.t("providerSuspended", provider.name), + content: this.i18nService.t("restoreProviderPortalAccessViaCustomerSupport"), + disableClose: true, + acceptButtonText: this.i18nService.t("contactSupportShort"), + cancelButtonText: null, + acceptAction: async () => { + window.open("https://bitwarden.com/contact/", "_blank"); + return Promise.resolve(); + }, + }); + } + }), + ); +} diff --git a/libs/common/src/enums/feature-flag.enum.ts b/libs/common/src/enums/feature-flag.enum.ts index ad61cb421cd..419872a6572 100644 --- a/libs/common/src/enums/feature-flag.enum.ts +++ b/libs/common/src/enums/feature-flag.enum.ts @@ -32,6 +32,7 @@ export enum FeatureFlag { UseOrganizationWarningsService = "use-organization-warnings-service", AllowTrialLengthZero = "pm-20322-allow-trial-length-0", PM21881_ManagePaymentDetailsOutsideCheckout = "pm-21881-manage-payment-details-outside-checkout", + PM21821_ProviderPortalTakeover = "pm-21821-provider-portal-takeover", /* Key Management */ PrivateKeyRegeneration = "pm-12241-private-key-regeneration", @@ -115,6 +116,7 @@ export const DefaultFeatureFlagValue = { [FeatureFlag.UseOrganizationWarningsService]: FALSE, [FeatureFlag.AllowTrialLengthZero]: FALSE, [FeatureFlag.PM21881_ManagePaymentDetailsOutsideCheckout]: FALSE, + [FeatureFlag.PM21821_ProviderPortalTakeover]: FALSE, /* Key Management */ [FeatureFlag.PrivateKeyRegeneration]: FALSE, diff --git a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts index f849fe81df6..c1044ba81e2 100644 --- a/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts +++ b/libs/components/src/dialog/simple-dialog/simple-configurable-dialog/simple-configurable-dialog.component.ts @@ -68,7 +68,9 @@ export class SimpleConfigurableDialogComponent { await this.simpleDialogOpts.acceptAction(); } - this.dialogRef.close(true); + if (!this.simpleDialogOpts.disableClose) { + this.dialogRef.close(true); + } }; private localizeText() {