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(); readonly additionalStorage = input(); readonly secretsManager = input<{ seats: LineItem; additionalServiceAccounts?: LineItem }>(); readonly estimatedTax = input.required(); // UI state readonly isExpanded = signal(true); /** * Calculates total for password manager line item */ readonly passwordManagerTotal = computed(() => { return this.passwordManager().quantity * this.passwordManager().cost; }); /** * Calculates total for additional storage line item if present */ readonly additionalStorageTotal = computed(() => { const storage = this.additionalStorage(); return storage ? storage.quantity * storage.cost : 0; }); /** * Calculates total for secrets manager seats if present */ readonly secretsManagerSeatsTotal = computed(() => { 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(() => { const sm = this.secretsManager(); return sm?.additionalServiceAccounts ? sm.additionalServiceAccounts.quantity * sm.additionalServiceAccounts.cost : 0; }); /** * Calculates the total of all line items */ readonly total = computed(() => 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() ); } }