1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 05:43:41 +00:00

[PM-22312] Resolve TS 5.8 errors (#16108)

* refactor: remove ts-strict-ignore and update

* refactor: remove ts-strict-ignore and update

* refactor: simplify if statement

* refactor: remove ts-strict-ignore and update

* refactor: add nullable union for interfaces
This commit is contained in:
Stephon Brown
2025-09-02 15:15:38 -04:00
committed by GitHub
parent 232dd89814
commit a4fca832f3
7 changed files with 105 additions and 75 deletions

View File

@@ -42,7 +42,7 @@ export type TrialOrganizationType = Exclude<ProductTierType, ProductTierType.Fre
export interface OrganizationInfo { export interface OrganizationInfo {
name: string; name: string;
email: string; email: string;
type: TrialOrganizationType; type: TrialOrganizationType | null;
} }
export interface OrganizationCreatedEvent { export interface OrganizationCreatedEvent {

View File

@@ -6,7 +6,7 @@
[showClose]="false" [showClose]="false"
*ngIf="freeTrialData?.shownBanner" *ngIf="freeTrialData?.shownBanner"
> >
{{ freeTrialData.message }} {{ freeTrialData?.message }}
<a <a
bitLink bitLink
linkType="secondary" linkType="secondary"

View File

@@ -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 { Location } from "@angular/common";
import { Component, OnDestroy } from "@angular/core"; import { Component, OnDestroy } from "@angular/core";
import { takeUntilDestroyed } from "@angular/core/rxjs-interop"; import { takeUntilDestroyed } from "@angular/core/rxjs-interop";
@@ -47,21 +45,21 @@ import { FreeTrial } from "../../types/free-trial";
standalone: false, standalone: false,
}) })
export class OrganizationPaymentMethodComponent implements OnDestroy { export class OrganizationPaymentMethodComponent implements OnDestroy {
organizationId: string; organizationId!: string;
isUnpaid = false; isUnpaid = false;
accountCredit: number; accountCredit?: number;
paymentSource?: PaymentSourceResponse; paymentSource?: PaymentSourceResponse;
subscriptionStatus?: string; subscriptionStatus?: string;
protected freeTrialData: FreeTrial; protected freeTrialData?: FreeTrial;
organization: Organization; organization?: Organization;
organizationSubscriptionResponse: OrganizationSubscriptionResponse; organizationSubscriptionResponse?: OrganizationSubscriptionResponse;
loading = true; loading = true;
protected readonly Math = Math; protected readonly Math = Math;
launchPaymentModalAutomatically = false; launchPaymentModalAutomatically = false;
protected taxInformation: TaxInformation; protected taxInformation?: TaxInformation;
constructor( constructor(
private activatedRoute: ActivatedRoute, private activatedRoute: ActivatedRoute,
@@ -104,7 +102,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
.subscribe(); .subscribe();
const state = this.router.getCurrentNavigation()?.extras?.state; 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(); const redundantState: any = location.getState();
const queryParam = this.activatedRoute.snapshot.queryParamMap.get( const queryParam = this.activatedRoute.snapshot.queryParamMap.get(
"launchPaymentModalAutomatically", "launchPaymentModalAutomatically",
@@ -116,10 +114,8 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
Object.prototype.hasOwnProperty.call(redundantState, "launchPaymentModalAutomatically") Object.prototype.hasOwnProperty.call(redundantState, "launchPaymentModalAutomatically")
) { ) {
this.launchPaymentModalAutomatically = redundantState.launchPaymentModalAutomatically; this.launchPaymentModalAutomatically = redundantState.launchPaymentModalAutomatically;
} else if (queryParam === "true") {
this.launchPaymentModalAutomatically = true;
} else { } else {
this.launchPaymentModalAutomatically = false; this.launchPaymentModalAutomatically = queryParam === "true";
} }
} }
ngOnDestroy(): void { ngOnDestroy(): void {
@@ -155,14 +151,21 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
this.paymentSource = paymentSource; this.paymentSource = paymentSource;
this.subscriptionStatus = subscriptionStatus; this.subscriptionStatus = subscriptionStatus;
this.taxInformation = taxInformation; this.taxInformation = taxInformation;
this.isUnpaid = this.subscriptionStatus === "unpaid";
if (this.organizationId) { if (this.organizationId) {
const organizationSubscriptionPromise = this.organizationApiService.getSubscription( const organizationSubscriptionPromise = this.organizationApiService.getSubscription(
this.organizationId, this.organizationId,
); );
const userId = await firstValueFrom( const userId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)), this.accountService.activeAccount$.pipe(map((a) => a?.id)),
); );
if (!userId) {
throw new Error("User ID is not found");
}
const organizationPromise = await firstValueFrom( const organizationPromise = await firstValueFrom(
this.organizationService this.organizationService
.organizations$(userId) .organizations$(userId)
@@ -173,15 +176,20 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
organizationSubscriptionPromise, organizationSubscriptionPromise,
organizationPromise, 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.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(
this.organization, this.organization,
this.organizationSubscriptionResponse, 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, // If the flag `launchPaymentModalAutomatically` is set to true,
// we schedule a timeout (delay of 800ms) to automatically launch the payment modal. // 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. // 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, { const dialogRef = TrialPaymentDialogComponent.open(this.dialogService, {
data: { data: {
organizationId: this.organizationId, organizationId: this.organizationId,
subscription: this.organizationSubscriptionResponse, subscription: this.organizationSubscriptionResponse!,
productTierType: this.organization?.productTierType, productTierType: this.organization!.productTierType,
}, },
}); });
const result = await lastValueFrom(dialogRef.closed); const result = await lastValueFrom(dialogRef.closed);
if (result === TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE.SUBMITTED) { if (result === TRIAL_PAYMENT_METHOD_DIALOG_RESULT_TYPE.SUBMITTED) {
this.location.replaceState(this.location.path(), "", {}); this.location.replaceState(this.location.path(), "", {});
if (this.launchPaymentModalAutomatically && !this.organization.enabled) { if (this.launchPaymentModalAutomatically && !this.organization?.enabled) {
await this.syncService.fullSync(true); await this.syncService.fullSync(true);
} }
this.launchPaymentModalAutomatically = false; this.launchPaymentModalAutomatically = false;
@@ -238,13 +246,14 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request); await this.billingApiService.verifyOrganizationBankAccount(this.organizationId, request);
this.toastService.showToast({ this.toastService.showToast({
variant: "success", variant: "success",
title: null, title: "",
message: this.i18nService.t("verifiedBankAccount"), message: this.i18nService.t("verifiedBankAccount"),
}); });
}; };
protected get accountCreditHeaderText(): string { 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); return this.i18nService.t(key);
} }
@@ -279,7 +288,7 @@ export class OrganizationPaymentMethodComponent implements OnDestroy {
if (!hasBillingAddress) { if (!hasBillingAddress) {
this.toastService.showToast({ this.toastService.showToast({
variant: "error", variant: "error",
title: null, title: "",
message: this.i18nService.t("billingAddressRequiredToAddCredit"), message: this.i18nService.t("billingAddressRequiredToAddCredit"),
}); });
return false; return false;

View File

@@ -24,7 +24,7 @@ import {
import { PaymentComponent } from "../payment/payment.component"; import { PaymentComponent } from "../payment/payment.component";
export interface AdjustPaymentDialogParams { export interface AdjustPaymentDialogParams {
initialPaymentMethod?: PaymentMethodType; initialPaymentMethod?: PaymentMethodType | null;
organizationId?: string; organizationId?: string;
productTier?: ProductTierType; productTier?: ProductTierType;
providerId?: string; providerId?: string;

View File

@@ -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 { Location } from "@angular/common";
import { Component, OnDestroy, OnInit } from "@angular/core"; import { Component, OnDestroy, OnInit } from "@angular/core";
import { FormBuilder, FormControl, Validators } from "@angular/forms"; import { FormBuilder, FormControl, Validators } from "@angular/forms";
@@ -42,21 +40,21 @@ import {
export class PaymentMethodComponent implements OnInit, OnDestroy { export class PaymentMethodComponent implements OnInit, OnDestroy {
loading = false; loading = false;
firstLoaded = false; firstLoaded = false;
billing: BillingPaymentResponse; billing?: BillingPaymentResponse;
org: OrganizationSubscriptionResponse; org?: OrganizationSubscriptionResponse;
sub: SubscriptionResponse; sub?: SubscriptionResponse;
paymentMethodType = PaymentMethodType; paymentMethodType = PaymentMethodType;
organizationId: string; organizationId?: string;
isUnpaid = false; isUnpaid = false;
organization: Organization; organization?: Organization;
verifyBankForm = this.formBuilder.group({ verifyBankForm = this.formBuilder.group({
amount1: new FormControl<number>(null, [ amount1: new FormControl<number>(0, [
Validators.required, Validators.required,
Validators.max(99), Validators.max(99),
Validators.min(0), Validators.min(0),
]), ]),
amount2: new FormControl<number>(null, [ amount2: new FormControl<number>(0, [
Validators.required, Validators.required,
Validators.max(99), Validators.max(99),
Validators.min(0), Validators.min(0),
@@ -64,7 +62,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
}); });
launchPaymentModalAutomatically = false; launchPaymentModalAutomatically = false;
protected freeTrialData: FreeTrial; protected freeTrialData?: FreeTrial;
constructor( constructor(
protected apiService: ApiService, protected apiService: ApiService,
@@ -84,7 +82,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
private configService: ConfigService, private configService: ConfigService,
) { ) {
const state = this.router.getCurrentNavigation()?.extras?.state; 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(); const redundantState: any = location.getState();
if (state && Object.prototype.hasOwnProperty.call(state, "launchPaymentModalAutomatically")) { if (state && Object.prototype.hasOwnProperty.call(state, "launchPaymentModalAutomatically")) {
this.launchPaymentModalAutomatically = state.launchPaymentModalAutomatically; this.launchPaymentModalAutomatically = state.launchPaymentModalAutomatically;
@@ -129,17 +127,23 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
} }
this.loading = true; this.loading = true;
if (this.forOrganization) { if (this.forOrganization) {
const billingPromise = this.organizationApiService.getBilling(this.organizationId); const billingPromise = this.organizationApiService.getBilling(this.organizationId!);
const organizationSubscriptionPromise = this.organizationApiService.getSubscription( const organizationSubscriptionPromise = this.organizationApiService.getSubscription(
this.organizationId, this.organizationId!,
); );
const userId = await firstValueFrom( const userId = await firstValueFrom(
this.accountService.activeAccount$.pipe(map((a) => a?.id)), this.accountService.activeAccount$.pipe(map((a) => a?.id)),
); );
if (!userId) {
throw new Error("User ID is not found");
}
const organizationPromise = await firstValueFrom( const organizationPromise = await firstValueFrom(
this.organizationService this.organizationService
.organizations$(userId) .organizations$(userId)
.pipe(getOrganizationById(this.organizationId)), .pipe(getOrganizationById(this.organizationId!)),
); );
[this.billing, this.org, this.organization] = await Promise.all([ [this.billing, this.org, this.organization] = await Promise.all([
@@ -171,14 +175,16 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
}; };
addCredit = async () => { addCredit = async () => {
const dialogRef = openAddCreditDialog(this.dialogService, { if (this.forOrganization) {
data: { const dialogRef = openAddCreditDialog(this.dialogService, {
organizationId: this.organizationId, data: {
}, organizationId: this.organizationId!,
}); },
const result = await lastValueFrom(dialogRef.closed); });
if (result === AddCreditDialogResult.Added) { const result = await lastValueFrom(dialogRef.closed);
await this.load(); if (result === AddCreditDialogResult.Added) {
await this.load();
}
} }
}; };
@@ -194,7 +200,7 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
if (result === AdjustPaymentDialogResultType.Submitted) { if (result === AdjustPaymentDialogResultType.Submitted) {
this.location.replaceState(this.location.path(), "", {}); this.location.replaceState(this.location.path(), "", {});
if (this.launchPaymentModalAutomatically && !this.organization.enabled) { if (this.launchPaymentModalAutomatically && !this.organization?.enabled) {
await this.syncService.fullSync(true); await this.syncService.fullSync(true);
} }
this.launchPaymentModalAutomatically = false; this.launchPaymentModalAutomatically = false;
@@ -208,18 +214,22 @@ export class PaymentMethodComponent implements OnInit, OnDestroy {
} }
const request = new VerifyBankRequest(); const request = new VerifyBankRequest();
request.amount1 = this.verifyBankForm.value.amount1; request.amount1 = this.verifyBankForm.value.amount1!;
request.amount2 = this.verifyBankForm.value.amount2; request.amount2 = this.verifyBankForm.value.amount2!;
await this.organizationApiService.verifyBank(this.organizationId, request); await this.organizationApiService.verifyBank(this.organizationId!, request);
this.toastService.showToast({ this.toastService.showToast({
variant: "success", variant: "success",
title: null, title: "",
message: this.i18nService.t("verifiedBankAccount"), message: this.i18nService.t("verifiedBankAccount"),
}); });
await this.load(); await this.load();
}; };
determineOrgsWithUpcomingPaymentIssues() { 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.freeTrialData = this.trialFlowService.checkForOrgsWithUpcomingPaymentIssues(
this.organization, this.organization,
this.org, this.org,

View File

@@ -37,7 +37,7 @@
bitButton bitButton
buttonType="primary" buttonType="primary"
[disabled]="orgInfoFormGroup.controls.name.invalid" [disabled]="orgInfoFormGroup.controls.name.invalid"
[loading]="loading && (trialPaymentOptional$ | async)" [loading]="loading && (trialPaymentOptional$ | async)!"
(click)="orgNameEntrySubmit()" (click)="orgNameEntrySubmit()"
> >
{{ {{
@@ -55,8 +55,8 @@
<app-trial-billing-step <app-trial-billing-step
*ngIf="stepper.selectedIndex === 2" *ngIf="stepper.selectedIndex === 2"
[organizationInfo]="{ [organizationInfo]="{
name: orgInfoFormGroup.value.name, name: orgInfoFormGroup.value.name!,
email: orgInfoFormGroup.value.billingEmail, email: orgInfoFormGroup.value.billingEmail!,
type: trialOrganizationType, type: trialOrganizationType,
}" }"
[subscriptionProduct]=" [subscriptionProduct]="

View File

@@ -1,5 +1,3 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { StepperSelectionEvent } from "@angular/cdk/stepper"; import { StepperSelectionEvent } from "@angular/cdk/stepper";
import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core"; import { Component, OnDestroy, OnInit, ViewChild } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms"; import { FormBuilder, Validators } from "@angular/forms";
@@ -50,15 +48,15 @@ export type InitiationPath =
standalone: false, standalone: false,
}) })
export class CompleteTrialInitiationComponent implements OnInit, OnDestroy { export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent; @ViewChild("stepper", { static: false }) verticalStepper!: VerticalStepperComponent;
inputPasswordFlow = InputPasswordFlow.SetInitialPasswordAccountRegistration; inputPasswordFlow = InputPasswordFlow.SetInitialPasswordAccountRegistration;
initializing = true; initializing = true;
/** Password Manager or Secrets Manager */ /** Password Manager or Secrets Manager */
product: ProductType; product?: ProductType;
/** The tier of product being subscribed to */ /** The tier of product being subscribed to */
productTier: ProductTierType; productTier!: ProductTierType;
/** Product types that display steppers for Password Manager */ /** Product types that display steppers for Password Manager */
stepperProductTypes: ProductTierType[] = [ stepperProductTypes: ProductTierType[] = [
ProductTierType.Teams, ProductTierType.Teams,
@@ -79,16 +77,16 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
orgId = ""; orgId = "";
orgLabel = ""; orgLabel = "";
billingSubLabel = ""; billingSubLabel = "";
enforcedPolicyOptions: MasterPasswordPolicyOptions; enforcedPolicyOptions?: MasterPasswordPolicyOptions;
/** User's email address associated with the trial */ /** User's email address associated with the trial */
email = ""; email = "";
/** Token from the backend associated with the email verification */ /** Token from the backend associated with the email verification */
emailVerificationToken: string; emailVerificationToken?: string;
loading = false; loading = false;
productTierValue: number; productTierValue?: ProductTierType;
trialLength: number; trialLength!: number;
orgInfoFormGroup = this.formBuilder.group({ orgInfoFormGroup = this.formBuilder.group({
name: ["", { validators: [Validators.required, Validators.maxLength(50)], updateOn: "change" }], name: ["", { validators: [Validators.required, Validators.maxLength(50)], updateOn: "change" }],
@@ -132,7 +130,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
// Show email validation toast when coming from email // Show email validation toast when coming from email
if (qParams.fromEmail && qParams.fromEmail === "true") { if (qParams.fromEmail && qParams.fromEmail === "true") {
this.toastService.showToast({ this.toastService.showToast({
title: null, title: "",
message: this.i18nService.t("emailVerifiedV2"), message: this.i18nService.t("emailVerifiedV2"),
variant: "success", variant: "success",
}); });
@@ -172,9 +170,15 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
}); });
const invite = await this.organizationInviteService.getOrganizationInvite(); const invite = await this.organizationInviteService.getOrganizationInvite();
let policies: Policy[] | null = null; let policies: Policy[] | undefined | null = null;
if (invite != null) { if (
invite != null &&
invite.organizationId &&
invite.token &&
invite.email &&
invite.organizationUserId
) {
try { try {
policies = await this.policyApiService.getPoliciesByToken( policies = await this.policyApiService.getPoliciesByToken(
invite.organizationId, invite.organizationId,
@@ -218,7 +222,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
if (event.selectedIndex === 1 && this.orgInfoFormGroup.controls.name.value === "") { if (event.selectedIndex === 1 && this.orgInfoFormGroup.controls.name.value === "") {
this.orgInfoSubLabel = this.planInfoLabel; this.orgInfoSubLabel = this.planInfoLabel;
} else if (event.previouslySelectedIndex === 1) { } else if (event.previouslySelectedIndex === 1) {
this.orgInfoSubLabel = this.orgInfoFormGroup.controls.name.value; this.orgInfoSubLabel = this.orgInfoFormGroup.controls.name.value!;
} }
} }
@@ -260,8 +264,11 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
} }
const organization: OrganizationInformation = { const organization: OrganizationInformation = {
name: this.orgInfoFormGroup.value.name, name: this.orgInfoFormGroup.value.name == null ? "" : this.orgInfoFormGroup.value.name,
billingEmail: this.orgInfoFormGroup.value.billingEmail, billingEmail:
this.orgInfoFormGroup.value.billingEmail == null
? ""
: this.orgInfoFormGroup.value.billingEmail,
initiationPath: trialInitiationPath, initiationPath: trialInitiationPath,
}; };
@@ -326,7 +333,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
} }
} }
get trialOrganizationType(): TrialOrganizationType { get trialOrganizationType(): TrialOrganizationType | null {
if (this.productTier === ProductTierType.Free) { if (this.productTier === ProductTierType.Free) {
return null; return null;
} }
@@ -352,8 +359,12 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
const response = await this.organizationBillingService.startFree({ const response = await this.organizationBillingService.startFree({
organization: { organization: {
name: this.orgInfoFormGroup.value.name, name: this.orgInfoFormGroup.value.name == null ? "" : this.orgInfoFormGroup.value.name,
billingEmail: this.orgInfoFormGroup.value.billingEmail, billingEmail:
this.orgInfoFormGroup.value.billingEmail == null
? ""
: this.orgInfoFormGroup.value.billingEmail,
initiationPath: "Password Manager trial from marketing website",
}, },
plan: { plan: {
type: 0, type: 0,
@@ -405,11 +416,11 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
await this.loginStrategyService.logIn(credentials); await this.loginStrategyService.logIn(credentials);
} }
finishRegistration(passwordInputResult: PasswordInputResult) { async finishRegistration(passwordInputResult: PasswordInputResult) {
this.submitting = true; this.submitting = true;
return this.registrationFinishService return this.registrationFinishService
.finishRegistration(this.email, passwordInputResult, this.emailVerificationToken) .finishRegistration(this.email, passwordInputResult, this.emailVerificationToken)
.catch((e) => { .catch((e: unknown): null => {
this.validationService.showError(e); this.validationService.showError(e);
this.submitting = false; this.submitting = false;
return null; return null;