1
0
mirror of https://github.com/bitwarden/browser synced 2025-12-11 13:53:34 +00:00

Pivot trial initiation copy based on trial length (#14635)

This commit is contained in:
Alex Morask
2025-05-08 10:44:28 -04:00
committed by GitHub
parent 360e731c95
commit fb01153bcf
9 changed files with 64 additions and 8 deletions

View File

@@ -56,7 +56,7 @@
</div> </div>
<div class="tw-flex tw-space-x-2"> <div class="tw-flex tw-space-x-2">
<button type="submit" buttonType="primary" bitButton [loading]="form.loading"> <button type="submit" buttonType="primary" bitButton [loading]="form.loading">
{{ "startTrial" | i18n }} {{ (trialLength > 0 ? "startTrial" : "submit") | i18n }}
</button> </button>
<button bitButton type="button" buttonType="secondary" (click)="stepBack()">Back</button> <button bitButton type="button" buttonType="secondary" (click)="stepBack()">Back</button>
</div> </div>

View File

@@ -55,6 +55,7 @@ export class TrialBillingStepComponent implements OnInit {
@ViewChild(ManageTaxInformationComponent) taxInfoComponent: ManageTaxInformationComponent; @ViewChild(ManageTaxInformationComponent) taxInfoComponent: ManageTaxInformationComponent;
@Input() organizationInfo: OrganizationInfo; @Input() organizationInfo: OrganizationInfo;
@Input() subscriptionProduct: SubscriptionProduct = SubscriptionProduct.PasswordManager; @Input() subscriptionProduct: SubscriptionProduct = SubscriptionProduct.PasswordManager;
@Input() trialLength: number;
@Output() steppedBack = new EventEmitter(); @Output() steppedBack = new EventEmitter();
@Output() organizationCreated = new EventEmitter<OrganizationCreatedEvent>(); @Output() organizationCreated = new EventEmitter<OrganizationCreatedEvent>();

View File

@@ -48,6 +48,7 @@
? SubscriptionProduct.SecretsManager ? SubscriptionProduct.SecretsManager
: SubscriptionProduct.PasswordManager : SubscriptionProduct.PasswordManager
" "
[trialLength]="trialLength"
(steppedBack)="previousStep()" (steppedBack)="previousStep()"
(organizationCreated)="createdOrganization($event)" (organizationCreated)="createdOrganization($event)"
> >
@@ -58,6 +59,7 @@
[email]="email" [email]="email"
[orgLabel]="orgLabel" [orgLabel]="orgLabel"
[product]="this.product" [product]="this.product"
[trialLength]="trialLength"
></app-trial-confirmation-details> ></app-trial-confirmation-details>
<div class="tw-mb-3 tw-flex"> <div class="tw-mb-3 tw-flex">
<a <a

View File

@@ -86,6 +86,8 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
loading = false; loading = false;
productTierValue: number; productTierValue: 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" }],
billingEmail: [""], billingEmail: [""],
@@ -160,6 +162,8 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
this.useTrialStepper = true; this.useTrialStepper = true;
} }
this.trialLength = qParams.trialLength ? parseInt(qParams.trialLength) : 7;
// Are they coming from an email for sponsoring a families organization // Are they coming from an email for sponsoring a families organization
// After logging in redirect them to setup the families sponsorship // After logging in redirect them to setup the families sponsorship
this.setupFamilySponsorship(qParams.sponsorshipToken); this.setupFamilySponsorship(qParams.sponsorshipToken);

View File

@@ -11,7 +11,11 @@ const route = {
const routerStateSnapshot = {} as RouterStateSnapshot; const routerStateSnapshot = {} as RouterStateSnapshot;
describe("freeTrialTextResolver", () => { describe("freeTrialTextResolver", () => {
it("shows password manager text", () => { beforeEach(() => {
route.queryParams = {};
});
it("shows password manager free trial text", () => {
route.queryParams.product = `${ProductType.PasswordManager}`; route.queryParams.product = `${ProductType.PasswordManager}`;
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({ expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
@@ -19,7 +23,16 @@ describe("freeTrialTextResolver", () => {
}); });
}); });
it("shows secret manager text", () => { it("shows password manager text", () => {
route.queryParams.product = `${ProductType.PasswordManager}`;
route.queryParams.trialLength = "0";
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
key: "continueSettingUpPasswordManager",
});
});
it("shows secret manager free trial text", () => {
route.queryParams.product = `${ProductType.SecretsManager}`; route.queryParams.product = `${ProductType.SecretsManager}`;
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({ expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
@@ -27,11 +40,29 @@ describe("freeTrialTextResolver", () => {
}); });
}); });
it("shows default text", () => { it("shows secret manager text", () => {
route.queryParams.product = `${ProductType.SecretsManager}`;
route.queryParams.trialLength = "0";
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
key: "continueSettingUpSecretsManager",
});
});
it("shows default free trial text", () => {
route.queryParams.product = `${ProductType.PasswordManager},${ProductType.SecretsManager}`; route.queryParams.product = `${ProductType.PasswordManager},${ProductType.SecretsManager}`;
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({ expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
key: "continueSettingUpFreeTrial", key: "continueSettingUpFreeTrial",
}); });
}); });
it("shows default text", () => {
route.queryParams.product = `${ProductType.PasswordManager},${ProductType.SecretsManager}`;
route.queryParams.trialLength = "0";
expect(freeTrialTextResolver(route, routerStateSnapshot)).toEqual({
key: "continueSettingUp",
});
});
}); });

View File

@@ -9,21 +9,29 @@ export const freeTrialTextResolver: ResolveFn<Translation | null> = (
const { product } = route.queryParams; const { product } = route.queryParams;
const products: ProductType[] = (product ?? "").split(",").map((p: string) => parseInt(p)); const products: ProductType[] = (product ?? "").split(",").map((p: string) => parseInt(p));
const trialLength = route.queryParams.trialLength ? parseInt(route.queryParams.trialLength) : 7;
const onlyPasswordManager = products.length === 1 && products[0] === ProductType.PasswordManager; const onlyPasswordManager = products.length === 1 && products[0] === ProductType.PasswordManager;
const onlySecretsManager = products.length === 1 && products[0] === ProductType.SecretsManager; const onlySecretsManager = products.length === 1 && products[0] === ProductType.SecretsManager;
switch (true) { switch (true) {
case onlyPasswordManager: case onlyPasswordManager:
return { return {
key: "continueSettingUpFreeTrialPasswordManager", key:
trialLength > 0
? "continueSettingUpFreeTrialPasswordManager"
: "continueSettingUpPasswordManager",
}; };
case onlySecretsManager: case onlySecretsManager:
return { return {
key: "continueSettingUpFreeTrialSecretsManager", key:
trialLength > 0
? "continueSettingUpFreeTrialSecretsManager"
: "continueSettingUpSecretsManager",
}; };
default: default:
return { return {
key: "continueSettingUpFreeTrial", key: trialLength > 0 ? "continueSettingUpFreeTrial" : "continueSettingUp",
}; };
} }
}; };

View File

@@ -13,7 +13,7 @@
>. >.
</p> </p>
</li> </li>
<li> <li *ngIf="trialLength > 0">
<p> <p>
{{ "trialPaidInfoMessage" | i18n: orgLabel }} {{ "trialPaidInfoMessage" | i18n: orgLabel }}
</p> </p>

View File

@@ -12,6 +12,7 @@ export class ConfirmationDetailsComponent {
@Input() email: string; @Input() email: string;
@Input() orgLabel: string; @Input() orgLabel: string;
@Input() product?: ProductType = ProductType.PasswordManager; @Input() product?: ProductType = ProductType.PasswordManager;
@Input() trialLength: number;
protected readonly Product = ProductType; protected readonly Product = ProductType;
} }

View File

@@ -9414,12 +9414,21 @@
"manageBillingFromProviderPortalMessage": { "manageBillingFromProviderPortalMessage": {
"message": "Manage billing from the Provider Portal" "message": "Manage billing from the Provider Portal"
}, },
"continueSettingUp": {
"message": "Continue setting up Bitwarden"
},
"continueSettingUpFreeTrial": { "continueSettingUpFreeTrial": {
"message": "Continue setting up your free trial of Bitwarden" "message": "Continue setting up your free trial of Bitwarden"
}, },
"continueSettingUpPasswordManager": {
"message": "Continue setting up Bitwarden Password Manager"
},
"continueSettingUpFreeTrialPasswordManager": { "continueSettingUpFreeTrialPasswordManager": {
"message": "Continue setting up your free trial of Bitwarden Password Manager" "message": "Continue setting up your free trial of Bitwarden Password Manager"
}, },
"continueSettingUpSecretsManager": {
"message": "Continue setting up Bitwarden Secrets Manager"
},
"continueSettingUpFreeTrialSecretsManager": { "continueSettingUpFreeTrialSecretsManager": {
"message": "Continue setting up your free trial of Bitwarden Secrets Manager" "message": "Continue setting up your free trial of Bitwarden Secrets Manager"
}, },