1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-12 22:33:35 +00:00
Files
browser/libs/pricing/src/components/cart-summary/cart-summary.component.ts
2025-10-23 15:02:01 -05:00

95 lines
2.9 KiB
TypeScript

import { CurrencyPipe } from "@angular/common";
import { Component, computed, input, signal } from "@angular/core";
import { TypographyModule, IconButtonModule } from "@bitwarden/components";
import { I18nPipe } from "@bitwarden/ui-common";
export type LineItem = {
quantity: number;
name: string;
cost: number;
cadence: "month" | "year";
};
/**
* A reusable UI-only component that displays a cart summary with line items.
* This component has no external dependencies and performs minimal logic -
* it only displays data and allows expanding/collapsing of line items.
*/
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
@Component({
selector: "billing-cart-summary",
templateUrl: "./cart-summary.component.html",
imports: [TypographyModule, IconButtonModule, CurrencyPipe, I18nPipe],
})
export class CartSummaryComponent {
// Required inputs
readonly passwordManager = input.required<LineItem>();
readonly additionalStorage = input<LineItem>();
readonly secretsManager = input<{ seats: LineItem; additionalServiceAccounts?: LineItem }>();
readonly estimatedTax = input.required<number>();
// UI state
readonly isExpanded = signal(true);
/**
* Calculates total for password manager line item
*/
readonly passwordManagerTotal = computed<number>(() => {
return this.passwordManager().quantity * this.passwordManager().cost;
});
/**
* Calculates total for additional storage line item if present
*/
readonly additionalStorageTotal = computed<number>(() => {
const storage = this.additionalStorage();
return storage ? storage.quantity * storage.cost : 0;
});
/**
* Calculates total for secrets manager seats if present
*/
readonly secretsManagerSeatsTotal = computed<number>(() => {
const sm = this.secretsManager();
return sm?.seats ? sm.seats.quantity * sm.seats.cost : 0;
});
/**
* Calculates total for secrets manager service accounts if present
*/
readonly additionalServiceAccountsTotal = computed<number>(() => {
const sm = this.secretsManager();
return sm?.additionalServiceAccounts
? sm.additionalServiceAccounts.quantity * sm.additionalServiceAccounts.cost
: 0;
});
/**
* Calculates the total of all line items
*/
readonly total = computed<number>(() => this.getTotalCost());
/**
* Toggles the expanded/collapsed state of the cart items
*/
toggleExpanded(): void {
this.isExpanded.update((value: boolean) => !value);
}
/**
* Gets the total cost of all line items in the cart
* @returns The total cost as a number
*/
private getTotalCost(): number {
return (
this.passwordManagerTotal() +
this.additionalStorageTotal() +
this.secretsManagerSeatsTotal() +
this.additionalServiceAccountsTotal() +
this.estimatedTax()
);
}
}