mirror of
https://github.com/bitwarden/browser.git
synced 2024-12-18 15:47:57 +01:00
[PM-15545][Defect] Update trial initiation UI for new flow via trial/send-verification-email endpoint (#12256)
* Add the on trial payment option on new UI * Rename variables correctly * Resolve the isTrialPaymentOptional and use observable * use firstValueFrom and remove subscribe * Resolve the selected plantype * Changes for free Org
This commit is contained in:
parent
30c151f44a
commit
bfa9cf3623
@ -23,12 +23,17 @@
|
|||||||
bitButton
|
bitButton
|
||||||
buttonType="primary"
|
buttonType="primary"
|
||||||
[disabled]="orgInfoFormGroup.controls.name.invalid"
|
[disabled]="orgInfoFormGroup.controls.name.invalid"
|
||||||
(click)="conditionallyCreateOrganization()"
|
[loading]="loading && (trialPaymentOptional$ | async)"
|
||||||
|
(click)="orgNameEntrySubmit()"
|
||||||
>
|
>
|
||||||
{{ "next" | i18n }}
|
{{ (trialPaymentOptional$ | async) ? ("startTrial" | i18n) : ("next" | i18n) }}
|
||||||
</button>
|
</button>
|
||||||
</app-vertical-step>
|
</app-vertical-step>
|
||||||
<app-vertical-step label="Billing" [subLabel]="billingSubLabel" *ngIf="!isSecretsManagerFree">
|
<app-vertical-step
|
||||||
|
label="Billing"
|
||||||
|
[subLabel]="billingSubLabel"
|
||||||
|
*ngIf="!(trialPaymentOptional$ | async) && !isSecretsManagerFree"
|
||||||
|
>
|
||||||
<app-trial-billing-step
|
<app-trial-billing-step
|
||||||
*ngIf="stepper.selectedIndex === 2"
|
*ngIf="stepper.selectedIndex === 2"
|
||||||
[organizationInfo]="{
|
[organizationInfo]="{
|
||||||
|
@ -4,7 +4,7 @@ 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";
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
import { ActivatedRoute, Router } from "@angular/router";
|
||||||
import { Subject, takeUntil } from "rxjs";
|
import { firstValueFrom, Subject, takeUntil } from "rxjs";
|
||||||
|
|
||||||
import { PasswordInputResult, RegistrationFinishService } from "@bitwarden/auth/angular";
|
import { PasswordInputResult, RegistrationFinishService } from "@bitwarden/auth/angular";
|
||||||
import { LoginStrategyServiceAbstraction, PasswordLoginCredentials } from "@bitwarden/auth/common";
|
import { LoginStrategyServiceAbstraction, PasswordLoginCredentials } from "@bitwarden/auth/common";
|
||||||
@ -12,8 +12,14 @@ import { PolicyApiServiceAbstraction } from "@bitwarden/common/admin-console/abs
|
|||||||
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
import { PolicyService } from "@bitwarden/common/admin-console/abstractions/policy/policy.service.abstraction";
|
||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
import { MasterPasswordPolicyOptions } from "@bitwarden/common/admin-console/models/domain/master-password-policy-options";
|
||||||
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
import { Policy } from "@bitwarden/common/admin-console/models/domain/policy";
|
||||||
import { OrganizationBillingServiceAbstraction as OrganizationBillingService } from "@bitwarden/common/billing/abstractions/organization-billing.service";
|
import {
|
||||||
import { ProductTierType, ProductType } from "@bitwarden/common/billing/enums";
|
OrganizationBillingServiceAbstraction as OrganizationBillingService,
|
||||||
|
OrganizationInformation,
|
||||||
|
PlanInformation,
|
||||||
|
} from "@bitwarden/common/billing/abstractions/organization-billing.service";
|
||||||
|
import { PlanType, ProductTierType, ProductType } from "@bitwarden/common/billing/enums";
|
||||||
|
import { FeatureFlag } from "@bitwarden/common/enums/feature-flag.enum";
|
||||||
|
import { ConfigService } from "@bitwarden/common/platform/abstractions/config/config.service";
|
||||||
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
import { I18nService } from "@bitwarden/common/platform/abstractions/i18n.service";
|
||||||
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
import { LogService } from "@bitwarden/common/platform/abstractions/log.service";
|
||||||
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
import { ValidationService } from "@bitwarden/common/platform/abstractions/validation.service";
|
||||||
@ -28,6 +34,10 @@ import { RouterService } from "../../../core/router.service";
|
|||||||
import { AcceptOrganizationInviteService } from "../../organization-invite/accept-organization.service";
|
import { AcceptOrganizationInviteService } from "../../organization-invite/accept-organization.service";
|
||||||
import { VerticalStepperComponent } from "../vertical-stepper/vertical-stepper.component";
|
import { VerticalStepperComponent } from "../vertical-stepper/vertical-stepper.component";
|
||||||
|
|
||||||
|
export type InitiationPath =
|
||||||
|
| "Password Manager trial from marketing website"
|
||||||
|
| "Secrets Manager trial from marketing website";
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-complete-trial-initiation",
|
selector: "app-complete-trial-initiation",
|
||||||
templateUrl: "complete-trial-initiation.component.html",
|
templateUrl: "complete-trial-initiation.component.html",
|
||||||
@ -65,6 +75,8 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
|||||||
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;
|
||||||
|
productTierValue: 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" }],
|
||||||
@ -74,6 +86,9 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
|||||||
private destroy$ = new Subject<void>();
|
private destroy$ = new Subject<void>();
|
||||||
protected readonly SubscriptionProduct = SubscriptionProduct;
|
protected readonly SubscriptionProduct = SubscriptionProduct;
|
||||||
protected readonly ProductType = ProductType;
|
protected readonly ProductType = ProductType;
|
||||||
|
protected trialPaymentOptional$ = this.configService.getFeatureFlag$(
|
||||||
|
FeatureFlag.TrialPaymentOptional,
|
||||||
|
);
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
protected router: Router,
|
protected router: Router,
|
||||||
@ -90,6 +105,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
|||||||
private registrationFinishService: RegistrationFinishService,
|
private registrationFinishService: RegistrationFinishService,
|
||||||
private validationService: ValidationService,
|
private validationService: ValidationService,
|
||||||
private loginStrategyService: LoginStrategyServiceAbstraction,
|
private loginStrategyService: LoginStrategyServiceAbstraction,
|
||||||
|
private configService: ConfigService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
@ -119,6 +135,7 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
|||||||
this.product = this.validProducts.includes(product) ? product : ProductType.PasswordManager;
|
this.product = this.validProducts.includes(product) ? product : ProductType.PasswordManager;
|
||||||
|
|
||||||
const productTierParam = parseInt(qParams.productTier) as ProductTierType;
|
const productTierParam = parseInt(qParams.productTier) as ProductTierType;
|
||||||
|
this.productTierValue = productTierParam;
|
||||||
|
|
||||||
/** Only show the trial stepper for a subset of types */
|
/** Only show the trial stepper for a subset of types */
|
||||||
const showPasswordManagerStepper = this.stepperProductTypes.includes(productTierParam);
|
const showPasswordManagerStepper = this.stepperProductTypes.includes(productTierParam);
|
||||||
@ -185,6 +202,16 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async orgNameEntrySubmit(): Promise<void> {
|
||||||
|
const isTrialPaymentOptional = await firstValueFrom(this.trialPaymentOptional$);
|
||||||
|
|
||||||
|
if (isTrialPaymentOptional) {
|
||||||
|
await this.createOrganizationOnTrial();
|
||||||
|
} else {
|
||||||
|
await this.conditionallyCreateOrganization();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/** Update local details from organization created event */
|
/** Update local details from organization created event */
|
||||||
createdOrganization(event: OrganizationCreatedEvent) {
|
createdOrganization(event: OrganizationCreatedEvent) {
|
||||||
this.orgId = event.organizationId;
|
this.orgId = event.organizationId;
|
||||||
@ -192,11 +219,62 @@ export class CompleteTrialInitiationComponent implements OnInit, OnDestroy {
|
|||||||
this.verticalStepper.next();
|
this.verticalStepper.next();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** create an organization on trial without payment method */
|
||||||
|
async createOrganizationOnTrial() {
|
||||||
|
this.loading = true;
|
||||||
|
let trialInitiationPath: InitiationPath = "Password Manager trial from marketing website";
|
||||||
|
let plan: PlanInformation = {
|
||||||
|
type: this.getPlanType(),
|
||||||
|
passwordManagerSeats: 1,
|
||||||
|
};
|
||||||
|
|
||||||
|
if (this.product === ProductType.SecretsManager) {
|
||||||
|
trialInitiationPath = "Secrets Manager trial from marketing website";
|
||||||
|
plan = {
|
||||||
|
...plan,
|
||||||
|
subscribeToSecretsManager: true,
|
||||||
|
isFromSecretsManagerTrial: true,
|
||||||
|
secretsManagerSeats: 1,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const organization: OrganizationInformation = {
|
||||||
|
name: this.orgInfoFormGroup.value.name,
|
||||||
|
billingEmail: this.orgInfoFormGroup.value.billingEmail,
|
||||||
|
initiationPath: trialInitiationPath,
|
||||||
|
};
|
||||||
|
|
||||||
|
const response = await this.organizationBillingService.purchaseSubscriptionNoPaymentMethod({
|
||||||
|
organization,
|
||||||
|
plan,
|
||||||
|
});
|
||||||
|
|
||||||
|
this.orgId = response?.id;
|
||||||
|
this.billingSubLabel = response.name.toString();
|
||||||
|
this.loading = false;
|
||||||
|
this.verticalStepper.next();
|
||||||
|
}
|
||||||
|
|
||||||
/** Move the user to the previous step */
|
/** Move the user to the previous step */
|
||||||
previousStep() {
|
previousStep() {
|
||||||
this.verticalStepper.previous();
|
this.verticalStepper.previous();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getPlanType() {
|
||||||
|
switch (this.productTier) {
|
||||||
|
case ProductTierType.Teams:
|
||||||
|
return PlanType.TeamsAnnually;
|
||||||
|
case ProductTierType.Enterprise:
|
||||||
|
return PlanType.EnterpriseAnnually;
|
||||||
|
case ProductTierType.Families:
|
||||||
|
return PlanType.FamiliesAnnually;
|
||||||
|
case ProductTierType.Free:
|
||||||
|
return PlanType.Free;
|
||||||
|
default:
|
||||||
|
return PlanType.EnterpriseAnnually;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
get isSecretsManagerFree() {
|
get isSecretsManagerFree() {
|
||||||
return this.product === ProductType.SecretsManager && this.productTier === ProductTierType.Free;
|
return this.product === ProductType.SecretsManager && this.productTier === ProductTierType.Free;
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user