diff --git a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts index 7e25a422477..20e69cf3bfd 100644 --- a/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts +++ b/apps/web/src/app/billing/accounts/trial-initiation/trial-billing-step.component.ts @@ -42,7 +42,7 @@ export type TrialOrganizationType = Exclude - {{ freeTrialData.message }} + {{ freeTrialData?.message }} a?.id)), ); + + if (!userId) { + throw new Error("User ID is not found"); + } + const organizationPromise = await firstValueFrom( this.organizationService .organizations$(userId) @@ -173,15 +176,20 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { organizationSubscriptionPromise, organizationPromise, ]); + + if (!this.organization) { + throw new Error("Organization is not found"); + } + if (!this.paymentSource) { + throw new Error("Payment source is not found"); + } + this.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues( this.organization, this.organizationSubscriptionResponse, - paymentSource, + this.paymentSource, ); } - // TODO: Eslint upgrade. Please resolve this since the ?? does nothing - // eslint-disable-next-line no-constant-binary-expression - this.isUnpaid = this.subscriptionStatus === "unpaid" ?? false; // If the flag `launchPaymentModalAutomatically` is set to true, // we schedule a timeout (delay of 800ms) to automatically launch the payment modal. // This delay ensures that any prior UI/rendering operations complete before triggering the modal. @@ -219,14 +227,14 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { const dialogRef = TrialPaymentDialogComponent.open(this.dialogService, { data: { organizationId: this.organizationId, - subscription: this.organizationSubscriptionResponse, - productTierType: this.organization?.productTierType, + subscription: this.organizationSubscriptionResponse!, + productTierType: this.organization!.productTierType, }, }); const result = await lastValueFrom(dialogRef.closed); if (result === TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE.SUBMITTED) { this.location.replaceState(this.location.path(), "", {}); - if (this.launchPaymentModalAutomatically && !this.organization.enabled) { + if (this.launchPaymentModalAutomatically && !this.organization?.enabled) { await this.syncService.fullSync(true); } this.launchPaymentModalAutomatically = false; @@ -238,13 +246,14 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request); this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("verifiedBankAccount"), }); }; protected get accountCreditHeaderText(): string { - const key = this.accountCredit <= 0 ? "accountBalance" : "accountCredit"; + const hasAccountCredit = this.accountCredit && this.accountCredit > 0; + const key = hasAccountCredit ? "accountCredit" : "accountBalance"; return this.i18nService.t(key); } @@ -279,7 +288,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy { if (!hasBillingAddress) { this.toastService.showToast({ variant: "error", - title: null, + title: "", message: this.i18nService.t("billingAddressRequiredToAddCredit"), }); return false; diff --git a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts index 94929c58656..9944085488f 100644 --- a/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts +++ b/apps/web/src/app/billing/shared/adjust-payment-dialog/adjust-payment-dialog.component.ts @@ -24,7 +24,7 @@ import { import { PaymentComponent } from "../payment/payment.component"; export interface AdjustPaymentDialogParams { - initialPaymentMethod?: PaymentMethodType; + initialPaymentMethod?: PaymentMethodType | null; organizationId?: string; productTier?: ProductTierType; providerId?: string; diff --git a/apps/web/src/app/billing/shared/payment-method.component.ts b/apps/web/src/app/billing/shared/payment-method.component.ts index 0e116b4f39a..91d5925669a 100644 --- a/apps/web/src/app/billing/shared/payment-method.component.ts +++ b/apps/web/src/app/billing/shared/payment-method.component.ts @@ -1,5 +1,3 @@ -// FIXME: Update this file to be type safe and remove this and next line -// @ts-strict-ignore import { Location } from "@angular/common"; import { Component, OnDestroy, OnInit } from "@angular/core"; import { FormBuilder, FormControl, Validators } from "@angular/forms"; @@ -42,21 +40,21 @@ import { export class PaymentMethodComponent implements OnInit, OnDestroy { loading = false; firstLoaded = false; - billing: BillingPaymentResponse; - org: OrganizationSubscriptionResponse; - sub: SubscriptionResponse; + billing?: BillingPaymentResponse; + org?: OrganizationSubscriptionResponse; + sub?: SubscriptionResponse; paymentMethodType = PaymentMethodType; - organizationId: string; + organizationId?: string; isUnpaid = false; - organization: Organization; + organization?: Organization; verifyBankForm = this.formBuilder.group({ - amount1: new FormControl(null, [ + amount1: new FormControl(0, [ Validators.required, Validators.max(99), Validators.min(0), ]), - amount2: new FormControl(null, [ + amount2: new FormControl(0, [ Validators.required, Validators.max(99), Validators.min(0), @@ -64,7 +62,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { }); launchPaymentModalAutomatically = false; - protected freeTrialData: FreeTrial; + protected freeTrialData?: FreeTrial; constructor( protected apiService: ApiService, @@ -84,7 +82,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { private configService: ConfigService, ) { const state = this.router.getCurrentNavigation()?.extras?.state; - // incase the above state is undefined or null we use redundantState + // In case the above state is undefined or null, we use redundantState const redundantState: any = location.getState(); if (state && Object.prototype.hasOwnProperty.call(state, "launchPaymentModalAutomatically")) { this.launchPaymentModalAutomatically = state.launchPaymentModalAutomatically; @@ -129,17 +127,23 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { } this.loading = true; if (this.forOrganization) { - const billingPromise = this.organizationApiService.getBilling(this.organizationId); + const billingPromise = this.organizationApiService.getBilling(this.organizationId!); const organizationSubscriptionPromise = this.organizationApiService.getSubscription( - this.organizationId, + this.organizationId!, ); + const userId = await firstValueFrom( this.accountService.activeAccount$.pipe(map((a) => a?.id)), ); + + if (!userId) { + throw new Error("User ID is not found"); + } + const organizationPromise = await firstValueFrom( this.organizationService .organizations$(userId) - .pipe(getOrganizationById(this.organizationId)), + .pipe(getOrganizationById(this.organizationId!)), ); [this.billing, this.org, this.organization] = await Promise.all([ @@ -171,14 +175,16 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { }; addCredit = async () => { - const dialogRef = openAddCreditDialog(this.dialogService, { - data: { - organizationId: this.organizationId, - }, - }); - const result = await lastValueFrom(dialogRef.closed); - if (result === AddCreditDialogResult.Added) { - await this.load(); + if (this.forOrganization) { + const dialogRef = openAddCreditDialog(this.dialogService, { + data: { + organizationId: this.organizationId!, + }, + }); + const result = await lastValueFrom(dialogRef.closed); + if (result === AddCreditDialogResult.Added) { + await this.load(); + } } }; @@ -194,7 +200,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { if (result === AdjustPaymentDialogResultType.Submitted) { this.location.replaceState(this.location.path(), "", {}); - if (this.launchPaymentModalAutomatically && !this.organization.enabled) { + if (this.launchPaymentModalAutomatically && !this.organization?.enabled) { await this.syncService.fullSync(true); } this.launchPaymentModalAutomatically = false; @@ -208,18 +214,22 @@ export class PaymentMethodComponent implements OnInit, OnDestroy { } const request = new VerifyBankRequest(); - request.amount1 = this.verifyBankForm.value.amount1; - request.amount2 = this.verifyBankForm.value.amount2; - await this.organizationApiService.verifyBank(this.organizationId, request); + request.amount1 = this.verifyBankForm.value.amount1!; + request.amount2 = this.verifyBankForm.value.amount2!; + await this.organizationApiService.verifyBank(this.organizationId!, request); this.toastService.showToast({ variant: "success", - title: null, + title: "", message: this.i18nService.t("verifiedBankAccount"), }); await this.load(); }; determineOrgsWithUpcomingPaymentIssues() { + if (!this.organization || !this.org || !this.billing) { + throw new Error("Organization, organization subscription, or billing is not defined"); + } + this.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues( this.organization, this.org, diff --git a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html index e74997cb9f5..c1a33a4c8df 100644 --- a/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html +++ b/apps/web/src/app/billing/trial-initiation/complete-trial-initiation/complete-trial-initiation.component.html @@ -37,7 +37,7 @@ bitButton buttonType="primary" [disabled]="orgInfoFormGroup.controls.name.invalid" - [loading]="loading && (trialPaymentOptional$ | async)" + [loading]="loading && (trialPaymentOptional$ | async)!" (click)="orgNameEntrySubmit()" > {{ @@ -55,8 +55,8 @@ { + .catch((e: unknown): null => { this.validationService.showError(e); this.submitting = false; return null;