Skip to content
Merged
Show file tree
Hide file tree
Changes from 12 commits
Commits
Show all changes
15 commits
Select commit Hold shift + click to select a range
51eaf59
feat(billing): update messages to add reasons
sbrown-livefront Nov 18, 2025
7ee8d51
feat(billing): update survey with switching reason based on param
sbrown-livefront Nov 18, 2025
346de88
Merge branch 'main' into billing/pm-26044/update-survey-for-user-and-โ€ฆ
sbrown-livefront Nov 18, 2025
9cc49df
Merge branch 'main' into billing/pm-26044/update-survey-for-user-and-โ€ฆ
sbrown-livefront Nov 18, 2025
5dcceb8
Merge branch 'main' into billing/pm-26044/update-survey-for-user-and-โ€ฆ
sbrown-livefront Nov 19, 2025
d2771ed
fix(billing): revert value of switching reasons
sbrown-livefront Nov 19, 2025
984d76b
Merge branch 'main' into billing/pm-26044/update-survey-for-user-and-โ€ฆ
sbrown-livefront Nov 19, 2025
fd9dd07
Merge branch 'main' into billing/pm-26044/update-survey-for-user-and-โ€ฆ
sbrown-livefront Nov 20, 2025
b50f0fb
fix(billing): revert removal of tooExpensive message
sbrown-livefront Nov 20, 2025
86ed8f2
fix(billing): Add plan type to params and update switching logic
sbrown-livefront Nov 20, 2025
bc177af
Merge branch 'main' into billing/pm-26044/update-survey-for-user-and-โ€ฆ
sbrown-livefront Nov 20, 2025
840750a
fix(billing): update to include logic
sbrown-livefront Nov 20, 2025
0a88aba
fix(billing): PR feedback
sbrown-livefront Nov 20, 2025
eb835fb
Merge branch 'main' into billing/pm-26044/update-survey-for-user-and-โ€ฆ
sbrown-livefront Nov 20, 2025
591f370
Merge branch 'main' into billing/pm-26044/update-survey-for-user-and-โ€ฆ
sbrown-livefront Nov 20, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,7 @@ export class OrganizationSubscriptionCloudComponent implements OnInit, OnDestroy
data: {
type: "Organization",
id: this.organizationId,
plan: this.sub.plan.type,
},
});

Expand Down
90 changes: 55 additions & 35 deletions apps/web/src/app/billing/shared/offboarding-survey.component.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
// FIXME: Update this file to be type safe and remove this and next line
// @ts-strict-ignore
import { Component, Inject } from "@angular/core";
import { ChangeDetectionStrategy, Component, Inject } from "@angular/core";
import { FormBuilder, Validators } from "@angular/forms";

import { BillingApiServiceAbstraction as BillingApiService } from "@bitwarden/common/billing/abstractions/billing-api.service.abstraction";
import { PlanType } from "@bitwarden/common/billing/enums";
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service";
import {
Expand All @@ -21,6 +22,7 @@ type UserOffboardingParams = {
type OrganizationOffboardingParams = {
type: "Organization";
id: string;
plan: PlanType;
};

export type OffboardingSurveyDialogParams = UserOffboardingParams | OrganizationOffboardingParams;
Expand All @@ -46,50 +48,20 @@ export const openOffboardingSurvey = (
dialogConfig,
);

// 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: "app-cancel-subscription-form",
templateUrl: "offboarding-survey.component.html",
changeDetection: ChangeDetectionStrategy.OnPush,
standalone: false,
})
export class OffboardingSurveyComponent {
protected ResultType = OffboardingSurveyDialogResultType;
protected readonly MaxFeedbackLength = 400;

protected readonly reasons: Reason[] = [
{
value: null,
text: this.i18nService.t("selectPlaceholder"),
},
{
value: "missing_features",
text: this.i18nService.t("missingFeatures"),
},
{
value: "switched_service",
text: this.i18nService.t("movingToAnotherTool"),
},
{
value: "too_complex",
text: this.i18nService.t("tooDifficultToUse"),
},
{
value: "unused",
text: this.i18nService.t("notUsingEnough"),
},
{
value: "too_expensive",
text: this.i18nService.t("tooExpensive"),
},
{
value: "other",
text: this.i18nService.t("other"),
},
];
protected readonly reasons: Reason[] = [];

protected formGroup = this.formBuilder.group({
reason: [this.reasons[0].value, [Validators.required]],
reason: [null, [Validators.required]],
feedback: ["", [Validators.maxLength(this.MaxFeedbackLength)]],
});

Expand All @@ -101,7 +73,35 @@ export class OffboardingSurveyComponent {
private i18nService: I18nService,
private platformUtilsService: PlatformUtilsService,
private toastService: ToastService,
) {}
) {
this.reasons = [
{
value: null,
text: this.i18nService.t("selectPlaceholder"),
},
{
value: "missing_features",
text: this.i18nService.t("missingFeatures"),
},
{
value: "switched_service",
text: this.i18nService.t("movingToAnotherTool"),
},
{
value: "too_complex",
text: this.i18nService.t("tooDifficultToUse"),
},
{
value: "unused",
text: this.i18nService.t("notUsingEnough"),
},
this.getSwitchingReason(),
{
value: "other",
text: this.i18nService.t("other"),
},
];
}

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

this.dialogRef.close(this.ResultType.Submitted);
};

private getSwitchingReason(): Reason {
if (this.dialogParams.type === "User") {
return {
value: "too_expensive",
text: this.i18nService.t("switchToFreePlan"),
};
}

const isFamilyPlan = [
PlanType.FamiliesAnnually,
PlanType.FamiliesAnnually2019,
PlanType.FamiliesAnnually2025,
].includes(this.dialogParams.plan);

return {
value: "too_expensive",
text: this.i18nService.t(isFamilyPlan ? "switchToFreeOrg" : "tooExpensive"),
};
}
}
8 changes: 8 additions & 0 deletions apps/web/src/locales/en/messages.json
Original file line number Diff line number Diff line change
Expand Up @@ -9824,6 +9824,14 @@
"message": "Too expensive",
"description": "An option for the offboarding survey shown when a user cancels their subscription."
},
"switchToFreePlan": {
"message": "Switching to free plan",
"description": "An option for the offboarding survey shown when a user cancels their subscription."
},
"switchToFreeOrg": {
"message": "Switching to free organization",
"description": "An option for the offboarding survey shown when a user cancels their subscription."
},
"freeForOneYear": {
"message": "Free for 1 year"
},
Expand Down
Loading