Skip to content

Commit 992f11f

Browse files
authored
Merge branch 'main' into PM-22143-Tools-Refactor-TS-Enums-to-be-const-objects
2 parents 57a7ba6 + 43897df commit 992f11f

9 files changed

+122
-89
lines changed

apps/web/src/app/billing/organizations/organization-subscription-cloud.component.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -344,6 +344,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
344344
data: {
345345
type: "Organization",
346346
id: this.organizationId,
347+
plan: this.sub.plan.type,
347348
},
348349
});
349350

apps/web/src/app/billing/shared/offboarding-survey.component.html

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,8 @@
2121
</bit-label>
2222
<textarea rows="4" bitInput formControlName="feedback"></textarea>
2323
<bit-hint>{{
24-
"charactersCurrentAndMaximum" | i18n: formGroup.value.feedback.length : MaxFeedbackLength
24+
"charactersCurrentAndMaximum"
25+
| i18n: formGroup.value.feedback?.length ?? 0 : MaxFeedbackLength
2526
}}</bit-hint>
2627
</bit-form-field>
2728
</div>

apps/web/src/app/billing/shared/offboarding-survey.component.ts

Lines changed: 55 additions & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,10 @@
11
// FIXME: Update this file to be type safe and remove this and next line
22
// @ts-strict-ignore
3-
import { Component, Inject } from "@angular/core";
3+
import { ChangeDetectionStrategy, Component, Inject } from "@angular/core";
44
import { FormBuilder, Validators } from "@angular/forms";
55

66
import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
7+
import { PlanType } from "@bitwarden/common/billing/enums";
78
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
89
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
910
import {
@@ -21,6 +22,7 @@ type UserOffboardingParams = {
2122
type OrganizationOffboardingParams = {
2223
type: "Organization";
2324
id: string;
25+
plan: PlanType;
2426
};
2527

2628
export type OffboardingSurveyDialogParams = UserOffboardingParams | OrganizationOffboardingParams;
@@ -46,50 +48,20 @@ export const openOffboardingSurvey = (
4648
dialogConfig,
4749
);
4850

49-
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
50-
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
5151
@Component({
5252
selector: "app-cancel-subscription-form",
5353
templateUrl: "offboarding-survey.component.html",
54+
changeDetection: ChangeDetectionStrategy.OnPush,
5455
standalone: false,
5556
})
5657
export class OffboardingSurveyComponent {
5758
protected ResultType = OffboardingSurveyDialogResultType;
5859
protected readonly MaxFeedbackLength = 400;
5960

60-
protected readonly reasons: Reason[] = [
61-
{
62-
value: null,
63-
text: this.i18nService.t("selectPlaceholder"),
64-
},
65-
{
66-
value: "missing_features",
67-
text: this.i18nService.t("missingFeatures"),
68-
},
69-
{
70-
value: "switched_service",
71-
text: this.i18nService.t("movingToAnotherTool"),
72-
},
73-
{
74-
value: "too_complex",
75-
text: this.i18nService.t("tooDifficultToUse"),
76-
},
77-
{
78-
value: "unused",
79-
text: this.i18nService.t("notUsingEnough"),
80-
},
81-
{
82-
value: "too_expensive",
83-
text: this.i18nService.t("tooExpensive"),
84-
},
85-
{
86-
value: "other",
87-
text: this.i18nService.t("other"),
88-
},
89-
];
61+
protected readonly reasons: Reason[] = [];
9062

9163
protected formGroup = this.formBuilder.group({
92-
reason: [this.reasons[0].value, [Validators.required]],
64+
reason: [null, [Validators.required]],
9365
feedback: ["", [Validators.maxLength(this.MaxFeedbackLength)]],
9466
});
9567

@@ -101,7 +73,35 @@ export class OffboardingSurveyComponent {
10173
private i18nService: I18nService,
10274
private platformUtilsService: PlatformUtilsService,
10375
private toastService: ToastService,
104-
) {}
76+
) {
77+
this.reasons = [
78+
{
79+
value: null,
80+
text: this.i18nService.t("selectPlaceholder"),
81+
},
82+
{
83+
value: "missing_features",
84+
text: this.i18nService.t("missingFeatures"),
85+
},
86+
{
87+
value: "switched_service",
88+
text: this.i18nService.t("movingToAnotherTool"),
89+
},
90+
{
91+
value: "too_complex",
92+
text: this.i18nService.t("tooDifficultToUse"),
93+
},
94+
{
95+
value: "unused",
96+
text: this.i18nService.t("notUsingEnough"),
97+
},
98+
this.getSwitchingReason(),
99+
{
100+
value: "other",
101+
text: this.i18nService.t("other"),
102+
},
103+
];
104+
}
105105

106106
submit = async () => {
107107
this.formGroup.markAllAsTouched();
@@ -127,4 +127,24 @@ export class OffboardingSurveyComponent {
127127

128128
this.dialogRef.close(this.ResultType.Submitted);
129129
};
130+
131+
private getSwitchingReason(): Reason {
132+
if (this.dialogParams.type === "User") {
133+
return {
134+
value: "too_expensive",
135+
text: this.i18nService.t("switchToFreePlan"),
136+
};
137+
}
138+
139+
const isFamilyPlan = [
140+
PlanType.FamiliesAnnually,
141+
PlanType.FamiliesAnnually2019,
142+
PlanType.FamiliesAnnually2025,
143+
].includes(this.dialogParams.plan);
144+
145+
return {
146+
value: "too_expensive",
147+
text: this.i18nService.t(isFamilyPlan ? "switchToFreeOrg" : "tooExpensive"),
148+
};
149+
}
130150
}

apps/web/src/app/dirt/reports/pages/cipher-report.component.ts

Lines changed: 14 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
// FIXME: Update this file to be type safe and remove this and next line
2-
// @ts-strict-ignore
31
import { Directive, OnDestroy } from "@angular/core";
42
import {
53
BehaviorSubject,
@@ -36,24 +34,24 @@ import {
3634
import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/services/admin-console-cipher-form-config.service";
3735

3836
@Directive()
39-
export class CipherReportComponent implements OnDestroy {
37+
export abstract class CipherReportComponent implements OnDestroy {
4038
isAdminConsoleActive = false;
4139

4240
loading = false;
4341
hasLoaded = false;
4442
ciphers: CipherView[] = [];
4543
allCiphers: CipherView[] = [];
4644
dataSource = new TableDataSource<CipherView>();
47-
organization: Organization;
48-
organizations: Organization[];
45+
organization: Organization | undefined = undefined;
46+
organizations: Organization[] = [];
4947
organizations$: Observable<Organization[]>;
5048

5149
filterStatus: any = [0];
5250
showFilterToggle: boolean = false;
5351
vaultMsg: string = "vault";
54-
currentFilterStatus: number | string;
52+
currentFilterStatus: number | string = 0;
5553
protected filterOrgStatus$ = new BehaviorSubject<number | string>(0);
56-
private destroyed$: Subject<void> = new Subject();
54+
protected destroyed$: Subject<void> = new Subject();
5755
private vaultItemDialogRef?: DialogRef<VaultItemDialogResult> | undefined;
5856

5957
constructor(
@@ -107,7 +105,7 @@ export class CipherReportComponent implements OnDestroy {
107105
if (filterId === 0) {
108106
cipherCount = this.allCiphers.length;
109107
} else if (filterId === 1) {
110-
cipherCount = this.allCiphers.filter((c) => c.organizationId === null).length;
108+
cipherCount = this.allCiphers.filter((c) => !c.organizationId).length;
111109
} else {
112110
this.organizations.filter((org: Organization) => {
113111
if (org.id === filterId) {
@@ -121,9 +119,9 @@ export class CipherReportComponent implements OnDestroy {
121119
}
122120

123121
async filterOrgToggle(status: any) {
124-
let filter = null;
122+
let filter = (c: CipherView) => true;
125123
if (typeof status === "number" && status === 1) {
126-
filter = (c: CipherView) => c.organizationId == null;
124+
filter = (c: CipherView) => !c.organizationId;
127125
} else if (typeof status === "string") {
128126
const orgId = status as OrganizationId;
129127
filter = (c: CipherView) => c.organizationId === orgId;
@@ -185,7 +183,7 @@ export class CipherReportComponent implements OnDestroy {
185183
cipher: CipherView,
186184
activeCollectionId?: CollectionId,
187185
) {
188-
const disableForm = cipher ? !cipher.edit && !this.organization.canEditAllCiphers : false;
186+
const disableForm = cipher ? !cipher.edit && !this.organization?.canEditAllCiphers : false;
189187

190188
this.vaultItemDialogRef = VaultItemDialogComponent.open(this.dialogService, {
191189
mode,
@@ -230,10 +228,11 @@ export class CipherReportComponent implements OnDestroy {
230228
let updatedCipher = await this.cipherService.get(cipher.id, activeUserId);
231229

232230
if (this.isAdminConsoleActive) {
233-
updatedCipher = await this.adminConsoleCipherFormConfigService.getCipher(
234-
cipher.id as CipherId,
235-
this.organization,
236-
);
231+
updatedCipher =
232+
(await this.adminConsoleCipherFormConfigService.getCipher(
233+
cipher.id as CipherId,
234+
this.organization!,
235+
)) ?? updatedCipher;
237236
}
238237

239238
// convert cipher to cipher view model

apps/web/src/app/dirt/reports/pages/exposed-passwords-report.component.spec.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -90,6 +90,7 @@ describe("ExposedPasswordsReportComponent", () => {
9090
});
9191

9292
beforeEach(() => {
93+
jest.clearAllMocks();
9394
fixture = TestBed.createComponent(ExposedPasswordsReportComponent);
9495
component = fixture.componentInstance;
9596
fixture.detectChanges();

apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.spec.ts

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
import { CUSTOM_ELEMENTS_SCHEMA } from "@angular/core";
12
import { ComponentFixture, TestBed } from "@angular/core/testing";
23
import { MockProxy, mock } from "jest-mock-extended";
34
import { of } from "rxjs";
@@ -29,14 +30,13 @@ describe("InactiveTwoFactorReportComponent", () => {
2930
const userId = Utils.newGuid() as UserId;
3031
const accountService: FakeAccountService = mockAccountServiceWith(userId);
3132

32-
beforeEach(() => {
33+
beforeEach(async () => {
3334
let cipherFormConfigServiceMock: MockProxy<CipherFormConfigService>;
3435
organizationService = mock<OrganizationService>();
3536
organizationService.organizations$.mockReturnValue(of([]));
3637
syncServiceMock = mock<SyncService>();
37-
// FIXME: Verify that this floating promise is intentional. If it is, add an explanatory comment and ensure there is proper error handling.
38-
// eslint-disable-next-line @typescript-eslint/no-floating-promises
39-
TestBed.configureTestingModule({
38+
39+
await TestBed.configureTestingModule({
4040
declarations: [InactiveTwoFactorReportComponent, I18nPipe],
4141
providers: [
4242
{
@@ -80,9 +80,7 @@ describe("InactiveTwoFactorReportComponent", () => {
8080
useValue: adminConsoleCipherFormConfigServiceMock,
8181
},
8282
],
83-
schemas: [],
84-
// FIXME(PM-18598): Replace unknownElements and unknownProperties with actual imports
85-
errorOnUnknownElements: false,
83+
schemas: [CUSTOM_ELEMENTS_SCHEMA],
8684
}).compileComponents();
8785
});
8886

apps/web/src/app/dirt/reports/pages/inactive-two-factor-report.component.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
1-
// FIXME: Update this file to be type safe and remove this and next line
2-
// @ts-strict-ignore
3-
import { Component, OnInit } from "@angular/core";
1+
import { ChangeDetectionStrategy, ChangeDetectorRef, Component, OnInit } from "@angular/core";
42

53
import { OrganizationService } from "@bitwarden/common/admin-console/abstractions/organization/organization.service.abstraction";
64
import { AccountService } from "@bitwarden/common/auth/abstractions/account.service";
@@ -19,9 +17,8 @@ import { AdminConsoleCipherFormConfigService } from "../../../vault/org-vault/se
1917

2018
import { CipherReportComponent } from "./cipher-report.component";
2119

22-
// FIXME(https://bitwarden.atlassian.net/browse/CL-764): Migrate to OnPush
23-
// eslint-disable-next-line @angular-eslint/prefer-on-push-component-change-detection
2420
@Component({
21+
changeDetection: ChangeDetectionStrategy.OnPush,
2522
selector: "app-inactive-two-factor-report",
2623
templateUrl: "inactive-two-factor-report.component.html",
2724
standalone: false,
@@ -42,6 +39,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl
4239
syncService: SyncService,
4340
cipherFormConfigService: CipherFormConfigService,
4441
adminConsoleCipherFormConfigService: AdminConsoleCipherFormConfigService,
42+
protected changeDetectorRef: ChangeDetectorRef,
4543
) {
4644
super(
4745
cipherService,
@@ -86,6 +84,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl
8684

8785
this.filterCiphersByOrg(inactive2faCiphers);
8886
this.cipherDocs = docs;
87+
this.changeDetectorRef.markForCheck();
8988
}
9089
}
9190

@@ -157,6 +156,7 @@ export class InactiveTwoFactorReportComponent extends CipherReportComponent impl
157156
}
158157
this.services.set(serviceData.domain, serviceData.documentation);
159158
}
159+
this.changeDetectorRef.markForCheck();
160160
}
161161

162162
/**

0 commit comments

Comments
 (0)