1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-16 08:13:42 +00:00

[AC-1758] Show banner when organization requires a payment method (#7088)

* Add billing banner states to account settings

* Add billing banner service

* Add add-payment-method-banners.component

* Use add-payment-method-banners.component in layouts

* Clear banner on payment method addition

* Ran prettier after CI update

* Finalize banners styling/translations

* Will's (non-Tailwind) feedback

* Review feedback

* Review feedback

* Review feedback

* Replace StateService with StateProvider in BillingBannerService

* Remove StateService methods
This commit is contained in:
Alex Morask
2024-01-23 12:47:52 -05:00
committed by GitHub
parent 4475f67bbc
commit 014281cb93
18 changed files with 208 additions and 8 deletions

View File

@@ -0,0 +1,15 @@
<ng-container *ngFor="let banner of banners$ | async">
<bit-banner
*ngIf="banner.visible"
bannerType="warning"
(onClose)="closeBanner(banner.organizationId)"
>
{{ "maintainYourSubscription" | i18n: banner.organizationName }}
<a
bitLink
linkType="contrast"
[routerLink]="['/organizations', banner.organizationId, 'billing', 'payment-method']"
>{{ "addAPaymentMethod" | i18n }}</a
>.
</bit-banner>
</ng-container>

View File

@@ -0,0 +1,76 @@
import { Component } from "@angular/core";
import { combineLatest, Observable, switchMap } from "rxjs";
import { OrganizationApiServiceAbstraction as OrganizationApiService } from "@bitwarden/common/admin-console/abstractions/organization/organization-api.service.abstraction";
import {
OrganizationService,
canAccessAdmin,
} from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
import { BillingBannerServiceAbstraction } from "@bitwarden/common/billing/abstractions/billing-banner.service.abstraction";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { BannerModule } from "@bitwarden/components";
import { SharedModule } from "../../shared/shared.module";
type PaymentMethodBannerData = {
organizationId: string;
organizationName: string;
visible: boolean;
};
@Component({
standalone: true,
selector: "app-payment-method-banners",
templateUrl: "payment-method-banners.component.html",
imports: [BannerModule, SharedModule],
})
export class PaymentMethodBannersComponent {
constructor(
private billingBannerService: BillingBannerServiceAbstraction,
private i18nService: I18nService,
private organizationService: OrganizationService,
private organizationApiService: OrganizationApiService,
) {}
private organizations$ = this.organizationService.memberOrganizations$.pipe(
canAccessAdmin(this.i18nService),
);
protected banners$: Observable<PaymentMethodBannerData[]> = combineLatest([
this.organizations$,
this.billingBannerService.paymentMethodBannerStates$,
]).pipe(
switchMap(async ([organizations, paymentMethodBannerStates]) => {
return await Promise.all(
organizations.map(async (organization) => {
const matchingBanner = paymentMethodBannerStates.find(
(banner) => banner.organizationId === organization.id,
);
if (matchingBanner !== null && matchingBanner !== undefined) {
return {
organizationId: organization.id,
organizationName: organization.name,
visible: matchingBanner.visible,
};
}
const response = await this.organizationApiService.risksSubscriptionFailure(
organization.id,
);
await this.billingBannerService.setPaymentMethodBannerState(
organization.id,
response.risksSubscriptionFailure,
);
return {
organizationId: organization.id,
organizationName: organization.name,
visible: response.risksSubscriptionFailure,
};
}),
);
}),
);
protected async closeBanner(organizationId: string): Promise<void> {
await this.billingBannerService.setPaymentMethodBannerState(organizationId, false);
}
}