[SG-483] Trial Initiation Cleanup (#4064)
* Remove 'showTrial' feature flag * Replace Register component with trial and redirect trial routes to register * Shore up fallback logic for bad params * Remove register component that is no longer used * Adjust register form margin top * Update unit tests for new param handling * Use enums for org names and add missing org routing * Add new tests and fix existing flaky ones * Use an enum for layouts
This commit is contained in:
parent
9c0aa9e8d4
commit
40cade39bb
|
@ -16,7 +16,6 @@
|
||||||
"proxyEvents": "https://events.bitwarden.com"
|
"proxyEvents": "https://events.bitwarden.com"
|
||||||
},
|
},
|
||||||
"flags": {
|
"flags": {
|
||||||
"showTrial": true,
|
"showPasswordless": false
|
||||||
"showPasswordless": true
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
"proxyNotifications": "http://localhost:61840"
|
"proxyNotifications": "http://localhost:61840"
|
||||||
},
|
},
|
||||||
"flags": {
|
"flags": {
|
||||||
"showTrial": true,
|
|
||||||
"secretsManager": true,
|
"secretsManager": true,
|
||||||
"showPasswordless": true
|
"showPasswordless": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,7 +6,5 @@
|
||||||
"proxyNotifications": "http://localhost:61841",
|
"proxyNotifications": "http://localhost:61841",
|
||||||
"port": 8081
|
"port": 8081
|
||||||
},
|
},
|
||||||
"flags": {
|
"flags": {}
|
||||||
"showTrial": false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,7 +10,6 @@
|
||||||
"proxyEvents": "https://events.qa.bitwarden.pw"
|
"proxyEvents": "https://events.qa.bitwarden.pw"
|
||||||
},
|
},
|
||||||
"flags": {
|
"flags": {
|
||||||
"showTrial": true,
|
|
||||||
"secretsManager": false,
|
"secretsManager": false,
|
||||||
"showPasswordless": true
|
"showPasswordless": true
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@
|
||||||
"port": 8081
|
"port": 8081
|
||||||
},
|
},
|
||||||
"flags": {
|
"flags": {
|
||||||
"showTrial": false,
|
|
||||||
"showPasswordless": false
|
"showPasswordless": false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,210 +0,0 @@
|
||||||
<div class="layout" [ngClass]="['layout', layout]">
|
|
||||||
<!-- TEAMS 1 Header -->
|
|
||||||
<header
|
|
||||||
class="header"
|
|
||||||
*ngIf="
|
|
||||||
layout === 'default' ||
|
|
||||||
layout === 'teams' ||
|
|
||||||
layout === 'teams1' ||
|
|
||||||
layout === 'teams2' ||
|
|
||||||
layout === 'enterprise' ||
|
|
||||||
layout === 'enterprise1' ||
|
|
||||||
layout === 'enterprise2' ||
|
|
||||||
layout === 'cnetcmpgnent' ||
|
|
||||||
layout === 'cnetcmpgnteams' ||
|
|
||||||
layout === 'cnetcmpgnind'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-7">
|
|
||||||
<img
|
|
||||||
alt="Bitwarden"
|
|
||||||
class="logo mb-2"
|
|
||||||
src="../../images/register-layout/logo-horizontal-white.svg"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</header>
|
|
||||||
|
|
||||||
<div class="container">
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-7" *ngIf="layout">
|
|
||||||
<div class="mt-5">
|
|
||||||
<!-- Default Body -->
|
|
||||||
<div
|
|
||||||
*ngIf="
|
|
||||||
layout === 'teams' ||
|
|
||||||
layout === 'enterprise' ||
|
|
||||||
layout === 'enterprise1' ||
|
|
||||||
layout === 'default'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<h1>The Bitwarden Password Manager</h1>
|
|
||||||
<h2>
|
|
||||||
Trusted by millions of individuals, teams, and organizations worldwide for secure
|
|
||||||
password storage and sharing.
|
|
||||||
</h2>
|
|
||||||
<p>Store logins, secure notes, and more</p>
|
|
||||||
<p>Collaborate and share securely</p>
|
|
||||||
<p>Access anywhere on any device</p>
|
|
||||||
<p>Create your account to get started</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- Teams & Enterprise Body -->
|
|
||||||
<div *ngIf="layout === 'teams1' || layout === 'teams2' || layout === 'enterprise2'">
|
|
||||||
<h1>
|
|
||||||
Start Your <span *ngIf="layout === 'teams1'">Teams<br /></span
|
|
||||||
><span *ngIf="layout === 'enterprise2'">Enterprise</span> Free Trial Now
|
|
||||||
</h1>
|
|
||||||
<h2>
|
|
||||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
|
||||||
password storage and sharing.
|
|
||||||
</h2>
|
|
||||||
<p>Collaborate and share securely</p>
|
|
||||||
<p>Deploy and manage quickly and easily</p>
|
|
||||||
<p>Access anywhere on any device</p>
|
|
||||||
<p>Create your account to get started</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CNET Campaign Teams & Enterprise Body -->
|
|
||||||
<div *ngIf="layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnent'">
|
|
||||||
<h1>
|
|
||||||
Start Your <span *ngIf="layout === 'cnetcmpgnteams'">Teams<br /></span
|
|
||||||
><span *ngIf="layout === 'cnetcmpgnent'">Enterprise</span> Free Trial Now
|
|
||||||
</h1>
|
|
||||||
<h2>
|
|
||||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
|
||||||
password storage and sharing.
|
|
||||||
</h2>
|
|
||||||
<p>Collaborate and share securely</p>
|
|
||||||
<p>Deploy and manage quickly and easily</p>
|
|
||||||
<p>Access anywhere on any device</p>
|
|
||||||
<p>Create your account to get started</p>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<!-- CNET Campaign Premium Body -->
|
|
||||||
<div *ngIf="layout === 'cnetcmpgnind'">
|
|
||||||
<h1>Start Your Premium Account Now</h1>
|
|
||||||
<h2>
|
|
||||||
Millions of individuals, teams, and organizations worldwide trust Bitwarden for secure
|
|
||||||
password storage and sharing.
|
|
||||||
</h2>
|
|
||||||
<p>Store logins, secure notes, and more</p>
|
|
||||||
<p>Secure your account with advanced two-step login</p>
|
|
||||||
<p>Access anywhere on any device</p>
|
|
||||||
<p>Create your account to get started</p>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div [ngClass]="{ 'col-5': layout, 'col-12': !layout }">
|
|
||||||
<div class="row justify-content-md-center mt-5">
|
|
||||||
<div [ngClass]="{ 'col-5': !layout, 'col-12': layout }">
|
|
||||||
<h1 class="lead text-center mb-4" *ngIf="!layout">{{ "createAccount" | i18n }}</h1>
|
|
||||||
<div class="card d-block">
|
|
||||||
<div class="card-body">
|
|
||||||
<app-callout
|
|
||||||
title="{{ 'createOrganizationStep1' | i18n }}"
|
|
||||||
type="info"
|
|
||||||
icon="bwi bwi-thumb-tack"
|
|
||||||
*ngIf="showCreateOrgMessage"
|
|
||||||
>
|
|
||||||
{{ "createOrganizationCreatePersonalAccount" | i18n }}
|
|
||||||
</app-callout>
|
|
||||||
<app-register-form
|
|
||||||
[queryParamEmail]="email"
|
|
||||||
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
|
||||||
[referenceDataValue]="referenceData"
|
|
||||||
></app-register-form>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div class="row">
|
|
||||||
<div class="col-7 d-flex align-items-center">
|
|
||||||
<div
|
|
||||||
*ngIf="
|
|
||||||
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<figure>
|
|
||||||
<figcaption>
|
|
||||||
<cite>
|
|
||||||
<img
|
|
||||||
src="../../images/register-layout/cnet-logo.svg"
|
|
||||||
class="w-25 d-block mx-auto"
|
|
||||||
alt="cnet logo"
|
|
||||||
/>
|
|
||||||
</cite>
|
|
||||||
</figcaption>
|
|
||||||
<blockquote class="mx-auto text-center px-4">
|
|
||||||
"No more excuses; start using Bitwarden today. The identity you save could be your
|
|
||||||
own. The money definitely will be."
|
|
||||||
</blockquote>
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div
|
|
||||||
*ngIf="
|
|
||||||
layout === 'teams' ||
|
|
||||||
layout === 'teams1' ||
|
|
||||||
layout === 'teams2' ||
|
|
||||||
layout === 'enterprise' ||
|
|
||||||
layout === 'enterprise1' ||
|
|
||||||
layout === 'enterprise2' ||
|
|
||||||
layout === 'default'
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<figure>
|
|
||||||
<figcaption>
|
|
||||||
<cite>
|
|
||||||
<img
|
|
||||||
src="../../images/register-layout/forbes-logo.svg"
|
|
||||||
class="w-25 d-block mx-auto"
|
|
||||||
alt="Forbes Logo"
|
|
||||||
/>
|
|
||||||
</cite>
|
|
||||||
</figcaption>
|
|
||||||
<blockquote class="mx-auto text-center px-4">
|
|
||||||
“Bitwarden boasts the backing of some of the world's best security experts and an
|
|
||||||
attractive, easy-to-use interface”
|
|
||||||
</blockquote>
|
|
||||||
</figure>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
*ngIf="
|
|
||||||
layout === 'cnetcmpgnent' || layout === 'cnetcmpgnteams' || layout === 'cnetcmpgnind'
|
|
||||||
"
|
|
||||||
class="col-5 d-flex align-items-center justify-content-center"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="../../images/register-layout/usnews-360-badge.svg"
|
|
||||||
class="w-50 d-block"
|
|
||||||
alt="US News 360 Reviews Best Password Manager"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
*ngIf="
|
|
||||||
layout === 'teams' ||
|
|
||||||
layout === 'teams1' ||
|
|
||||||
layout === 'teams2' ||
|
|
||||||
layout === 'enterprise' ||
|
|
||||||
layout === 'enterprise1' ||
|
|
||||||
layout === 'enterprise2' ||
|
|
||||||
layout === 'default'
|
|
||||||
"
|
|
||||||
class="col-5 d-flex align-items-center justify-content-center"
|
|
||||||
>
|
|
||||||
<img
|
|
||||||
src="../../images/register-layout/usnews-360-badge.svg"
|
|
||||||
class="w-50 d-block"
|
|
||||||
alt="US News 360 Reviews Best Password Manager"
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
|
@ -1,149 +0,0 @@
|
||||||
import { Component, OnDestroy, OnInit } from "@angular/core";
|
|
||||||
import { UntypedFormBuilder } from "@angular/forms";
|
|
||||||
import { ActivatedRoute, Router } from "@angular/router";
|
|
||||||
import { Subject, takeUntil } from "rxjs";
|
|
||||||
import { first } from "rxjs/operators";
|
|
||||||
|
|
||||||
import { RegisterComponent as BaseRegisterComponent } from "@bitwarden/angular/components/register.component";
|
|
||||||
import { ApiService } from "@bitwarden/common/abstractions/api.service";
|
|
||||||
import { AuthService } from "@bitwarden/common/abstractions/auth.service";
|
|
||||||
import { CryptoService } from "@bitwarden/common/abstractions/crypto.service";
|
|
||||||
import { EnvironmentService } from "@bitwarden/common/abstractions/environment.service";
|
|
||||||
import { FormValidationErrorsService } from "@bitwarden/common/abstractions/formValidationErrors.service";
|
|
||||||
import { I18nService } from "@bitwarden/common/abstractions/i18n.service";
|
|
||||||
import { LogService } from "@bitwarden/common/abstractions/log.service";
|
|
||||||
import { PasswordGenerationService } from "@bitwarden/common/abstractions/passwordGeneration.service";
|
|
||||||
import { PlatformUtilsService } from "@bitwarden/common/abstractions/platformUtils.service";
|
|
||||||
import { PolicyApiServiceAbstraction } from "@bitwarden/common/abstractions/policy/policy-api.service.abstraction";
|
|
||||||
import { PolicyService } from "@bitwarden/common/abstractions/policy/policy.service.abstraction";
|
|
||||||
import { StateService } from "@bitwarden/common/abstractions/state.service";
|
|
||||||
import { PolicyData } from "@bitwarden/common/models/data/policy.data";
|
|
||||||
import { MasterPasswordPolicyOptions } from "@bitwarden/common/models/domain/master-password-policy-options";
|
|
||||||
import { Policy } from "@bitwarden/common/models/domain/policy";
|
|
||||||
import { ReferenceEventRequest } from "@bitwarden/common/models/request/reference-event.request";
|
|
||||||
|
|
||||||
import { RouterService } from "../core";
|
|
||||||
|
|
||||||
@Component({
|
|
||||||
selector: "app-register",
|
|
||||||
templateUrl: "register.component.html",
|
|
||||||
})
|
|
||||||
export class RegisterComponent extends BaseRegisterComponent implements OnInit, OnDestroy {
|
|
||||||
email = "";
|
|
||||||
showCreateOrgMessage = false;
|
|
||||||
layout = "";
|
|
||||||
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
|
||||||
|
|
||||||
private policies: Policy[];
|
|
||||||
private destroy$ = new Subject<void>();
|
|
||||||
|
|
||||||
constructor(
|
|
||||||
formValidationErrorService: FormValidationErrorsService,
|
|
||||||
formBuilder: UntypedFormBuilder,
|
|
||||||
authService: AuthService,
|
|
||||||
router: Router,
|
|
||||||
i18nService: I18nService,
|
|
||||||
cryptoService: CryptoService,
|
|
||||||
apiService: ApiService,
|
|
||||||
private route: ActivatedRoute,
|
|
||||||
stateService: StateService,
|
|
||||||
platformUtilsService: PlatformUtilsService,
|
|
||||||
passwordGenerationService: PasswordGenerationService,
|
|
||||||
private policyApiService: PolicyApiServiceAbstraction,
|
|
||||||
private policyService: PolicyService,
|
|
||||||
environmentService: EnvironmentService,
|
|
||||||
logService: LogService,
|
|
||||||
private routerService: RouterService
|
|
||||||
) {
|
|
||||||
super(
|
|
||||||
formValidationErrorService,
|
|
||||||
formBuilder,
|
|
||||||
authService,
|
|
||||||
router,
|
|
||||||
i18nService,
|
|
||||||
cryptoService,
|
|
||||||
apiService,
|
|
||||||
stateService,
|
|
||||||
platformUtilsService,
|
|
||||||
passwordGenerationService,
|
|
||||||
environmentService,
|
|
||||||
logService
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
async ngOnInit() {
|
|
||||||
// eslint-disable-next-line rxjs-angular/prefer-takeuntil
|
|
||||||
this.route.queryParams.pipe(first()).subscribe((qParams) => {
|
|
||||||
this.referenceData = new ReferenceEventRequest();
|
|
||||||
if (qParams.email != null && qParams.email.indexOf("@") > -1) {
|
|
||||||
this.email = qParams.email;
|
|
||||||
}
|
|
||||||
if (qParams.premium != null) {
|
|
||||||
this.routerService.setPreviousUrl("/settings/premium");
|
|
||||||
} else if (qParams.org != null) {
|
|
||||||
this.showCreateOrgMessage = true;
|
|
||||||
this.referenceData.flow = qParams.org;
|
|
||||||
const route = this.router.createUrlTree(["create-organization"], {
|
|
||||||
queryParams: { plan: qParams.org },
|
|
||||||
});
|
|
||||||
this.routerService.setPreviousUrl(route.toString());
|
|
||||||
}
|
|
||||||
if (qParams.layout != null) {
|
|
||||||
this.layout = this.referenceData.layout = qParams.layout;
|
|
||||||
}
|
|
||||||
if (qParams.reference != null) {
|
|
||||||
this.referenceData.id = qParams.reference;
|
|
||||||
} else {
|
|
||||||
this.referenceData.id = ("; " + document.cookie)
|
|
||||||
.split("; reference=")
|
|
||||||
.pop()
|
|
||||||
.split(";")
|
|
||||||
.shift();
|
|
||||||
}
|
|
||||||
// Are they coming from an email for sponsoring a families organization
|
|
||||||
if (qParams.sponsorshipToken != null) {
|
|
||||||
// After logging in redirect them to setup the families sponsorship
|
|
||||||
const route = this.router.createUrlTree(["setup/families-for-enterprise"], {
|
|
||||||
queryParams: { plan: qParams.sponsorshipToken },
|
|
||||||
});
|
|
||||||
this.routerService.setPreviousUrl(route.toString());
|
|
||||||
}
|
|
||||||
if (this.referenceData.id === "") {
|
|
||||||
this.referenceData.id = null;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
const invite = await this.stateService.getOrganizationInvitation();
|
|
||||||
if (invite != null) {
|
|
||||||
try {
|
|
||||||
const policies = await this.policyApiService.getPoliciesByToken(
|
|
||||||
invite.organizationId,
|
|
||||||
invite.token,
|
|
||||||
invite.email,
|
|
||||||
invite.organizationUserId
|
|
||||||
);
|
|
||||||
if (policies.data != null) {
|
|
||||||
const policiesData = policies.data.map((p) => new PolicyData(p));
|
|
||||||
this.policies = policiesData.map((p) => new Policy(p));
|
|
||||||
}
|
|
||||||
} catch (e) {
|
|
||||||
this.logService.error(e);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (this.policies != null) {
|
|
||||||
this.policyService
|
|
||||||
.masterPasswordPolicyOptions$(this.policies)
|
|
||||||
.pipe(takeUntil(this.destroy$))
|
|
||||||
.subscribe((enforcedPasswordPolicyOptions) => {
|
|
||||||
this.enforcedPolicyOptions = enforcedPasswordPolicyOptions;
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
await super.ngOnInit();
|
|
||||||
}
|
|
||||||
|
|
||||||
ngOnDestroy(): void {
|
|
||||||
this.destroy$.next();
|
|
||||||
this.destroy$.complete();
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -23,26 +23,39 @@
|
||||||
|
|
||||||
<div class="tw-pt-12">
|
<div class="tw-pt-12">
|
||||||
<!-- Layout params are used by marketing to determine left-hand content -->
|
<!-- Layout params are used by marketing to determine left-hand content -->
|
||||||
<app-default-content *ngIf="layout === 'default'"></app-default-content>
|
<app-default-content *ngIf="layout === layouts.default"></app-default-content>
|
||||||
<app-teams-content *ngIf="layout === 'teams'"></app-teams-content>
|
<app-teams-content *ngIf="layout === layouts.teams"></app-teams-content>
|
||||||
<app-teams1-content *ngIf="layout === 'teams1'"></app-teams1-content>
|
<app-teams1-content *ngIf="layout === layouts.teams1"></app-teams1-content>
|
||||||
<app-teams2-content *ngIf="layout === 'teams2'"></app-teams2-content>
|
<app-teams2-content *ngIf="layout === layouts.teams2"></app-teams2-content>
|
||||||
<app-enterprise-content *ngIf="layout === 'enterprise'"></app-enterprise-content>
|
<app-enterprise-content *ngIf="layout === layouts.enterprise"></app-enterprise-content>
|
||||||
<app-enterprise1-content *ngIf="layout === 'enterprise1'"></app-enterprise1-content>
|
<app-enterprise1-content *ngIf="layout === layouts.enterprise1"></app-enterprise1-content>
|
||||||
<app-enterprise2-content *ngIf="layout === 'enterprise2'"></app-enterprise2-content>
|
<app-enterprise2-content *ngIf="layout === layouts.enterprise2"></app-enterprise2-content>
|
||||||
<app-cnet-enterprise-content
|
<app-cnet-enterprise-content
|
||||||
*ngIf="layout === 'cnetcmpgnent'"
|
*ngIf="layout === layouts.cnetcmpgnent"
|
||||||
></app-cnet-enterprise-content>
|
></app-cnet-enterprise-content>
|
||||||
<app-cnet-individual-content
|
<app-cnet-individual-content
|
||||||
*ngIf="layout === 'cnetcmpgnind'"
|
*ngIf="layout === layouts.cnetcmpgnind"
|
||||||
></app-cnet-individual-content>
|
></app-cnet-individual-content>
|
||||||
<app-cnet-teams-content *ngIf="layout === 'cnetcmpgnteams'"></app-cnet-teams-content>
|
<app-cnet-teams-content *ngIf="layout === layouts.cnetcmpgnteams"></app-cnet-teams-content>
|
||||||
<app-abm-enterprise-content *ngIf="layout === 'abmenterprise'"></app-abm-enterprise-content>
|
<app-abm-enterprise-content
|
||||||
<app-abm-teams-content *ngIf="layout === 'abmteams'"></app-abm-teams-content>
|
*ngIf="layout === layouts.abmenterprise"
|
||||||
|
></app-abm-enterprise-content>
|
||||||
|
<app-abm-teams-content *ngIf="layout === layouts.abmteams"></app-abm-teams-content>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="tw-w-1/2">
|
<div class="tw-w-1/2">
|
||||||
<div class="tw-pt-56">
|
<div *ngIf="!useTrialStepper">
|
||||||
|
<div
|
||||||
|
class="tw-min-w-xl tw-m-auto tw-mt-28 tw-max-w-xl tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-background tw-p-8"
|
||||||
|
>
|
||||||
|
<app-register-form
|
||||||
|
[queryParamEmail]="email"
|
||||||
|
[enforcedPolicyOptions]="enforcedPolicyOptions"
|
||||||
|
[referenceDataValue]="referenceData"
|
||||||
|
></app-register-form>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="tw-pt-44" *ngIf="useTrialStepper">
|
||||||
<div class="tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-background">
|
<div class="tw-rounded tw-border tw-border-solid tw-border-secondary-300 tw-bg-background">
|
||||||
<div class="tw-flex tw-h-12 tw-w-full tw-items-center tw-rounded-t tw-bg-secondary-100">
|
<div class="tw-flex tw-h-12 tw-w-full tw-items-center tw-rounded-t tw-bg-secondary-100">
|
||||||
<h2 class="tw-mb-0 tw-pl-4 tw-text-base tw-font-bold tw-uppercase">
|
<h2 class="tw-mb-0 tw-pl-4 tw-text-base tw-font-bold tw-uppercase">
|
||||||
|
|
|
@ -168,8 +168,9 @@ describe("TrialInitiationComponent", () => {
|
||||||
it("should set org variable to be enterprise and plan to EnterpriseAnnually if org param is enterprise", fakeAsync(() => {
|
it("should set org variable to be enterprise and plan to EnterpriseAnnually if org param is enterprise", fakeAsync(() => {
|
||||||
mockQueryParams.next({ org: "enterprise" });
|
mockQueryParams.next({ org: "enterprise" });
|
||||||
tick(); // wait for resolution
|
tick(); // wait for resolution
|
||||||
|
fixture = TestBed.createComponent(TrialInitiationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.ngOnInit();
|
|
||||||
expect(component.org).toBe("enterprise");
|
expect(component.org).toBe("enterprise");
|
||||||
expect(component.plan).toBe(PlanType.EnterpriseAnnually);
|
expect(component.plan).toBe(PlanType.EnterpriseAnnually);
|
||||||
}));
|
}));
|
||||||
|
@ -182,13 +183,33 @@ describe("TrialInitiationComponent", () => {
|
||||||
expect(component.org).toBe("");
|
expect(component.org).toBe("");
|
||||||
expect(component.accountCreateOnly).toBe(true);
|
expect(component.accountCreateOnly).toBe(true);
|
||||||
}));
|
}));
|
||||||
it("should set the org to be families and plan to FamiliesAnnually if org param is invalid ", fakeAsync(async () => {
|
it("should not set the org if org param is invalid ", fakeAsync(async () => {
|
||||||
mockQueryParams.next({ org: "hahahaha" });
|
mockQueryParams.next({ org: "hahahaha" });
|
||||||
tick(); // wait for resolution
|
tick(); // wait for resolution
|
||||||
|
fixture = TestBed.createComponent(TrialInitiationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.org).toBe("");
|
||||||
|
expect(component.accountCreateOnly).toBe(true);
|
||||||
|
}));
|
||||||
|
it("should set the layout variable if layout param is valid ", fakeAsync(async () => {
|
||||||
|
mockQueryParams.next({ layout: "teams1" });
|
||||||
|
tick(); // wait for resolution
|
||||||
|
fixture = TestBed.createComponent(TrialInitiationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
|
fixture.detectChanges();
|
||||||
|
expect(component.layout).toBe("teams1");
|
||||||
|
expect(component.accountCreateOnly).toBe(false);
|
||||||
|
}));
|
||||||
|
it("should not set the layout variable and leave as 'default' if layout param is invalid ", fakeAsync(async () => {
|
||||||
|
mockQueryParams.next({ layout: "asdfasdf" });
|
||||||
|
tick(); // wait for resolution
|
||||||
|
fixture = TestBed.createComponent(TrialInitiationComponent);
|
||||||
|
component = fixture.componentInstance;
|
||||||
fixture.detectChanges();
|
fixture.detectChanges();
|
||||||
component.ngOnInit();
|
component.ngOnInit();
|
||||||
expect(component.org).toBe("families");
|
expect(component.layout).toBe("default");
|
||||||
expect(component.plan).toBe(PlanType.FamiliesAnnually);
|
expect(component.accountCreateOnly).toBe(true);
|
||||||
}));
|
}));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -20,6 +20,30 @@ import { ReferenceEventRequest } from "@bitwarden/common/models/request/referenc
|
||||||
import { RouterService } from "./../../core/router.service";
|
import { RouterService } from "./../../core/router.service";
|
||||||
import { VerticalStepperComponent } from "./vertical-stepper/vertical-stepper.component";
|
import { VerticalStepperComponent } from "./vertical-stepper/vertical-stepper.component";
|
||||||
|
|
||||||
|
enum ValidOrgParams {
|
||||||
|
families = "families",
|
||||||
|
enterprise = "enterprise",
|
||||||
|
teams = "teams",
|
||||||
|
individual = "individual",
|
||||||
|
premium = "premium",
|
||||||
|
free = "free",
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ValidLayoutParams {
|
||||||
|
default = "default",
|
||||||
|
teams = "teams",
|
||||||
|
teams1 = "teams1",
|
||||||
|
teams2 = "teams2",
|
||||||
|
enterprise = "enterprise",
|
||||||
|
enterprise1 = "enterprise1",
|
||||||
|
enterprise2 = "enterprise2",
|
||||||
|
cnetcmpgnent = "cnetcmpgnent",
|
||||||
|
cnetcmpgnind = "cnetcmpgnind",
|
||||||
|
cnetcmpgnteams = "cnetcmpgnteams",
|
||||||
|
abmenterprise = "abmenterprise",
|
||||||
|
abmteams = "abmteams",
|
||||||
|
}
|
||||||
|
|
||||||
@Component({
|
@Component({
|
||||||
selector: "app-trial",
|
selector: "app-trial",
|
||||||
templateUrl: "trial-initiation.component.html",
|
templateUrl: "trial-initiation.component.html",
|
||||||
|
@ -35,9 +59,20 @@ export class TrialInitiationComponent implements OnInit, OnDestroy {
|
||||||
plan: PlanType;
|
plan: PlanType;
|
||||||
product: ProductType;
|
product: ProductType;
|
||||||
accountCreateOnly = true;
|
accountCreateOnly = true;
|
||||||
|
useTrialStepper = false;
|
||||||
policies: Policy[];
|
policies: Policy[];
|
||||||
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
enforcedPolicyOptions: MasterPasswordPolicyOptions;
|
||||||
validOrgs: string[] = ["teams", "enterprise", "families"];
|
trialFlowOrgs: string[] = [
|
||||||
|
ValidOrgParams.teams,
|
||||||
|
ValidOrgParams.enterprise,
|
||||||
|
ValidOrgParams.families,
|
||||||
|
];
|
||||||
|
routeFlowOrgs: string[] = [
|
||||||
|
ValidOrgParams.free,
|
||||||
|
ValidOrgParams.premium,
|
||||||
|
ValidOrgParams.individual,
|
||||||
|
];
|
||||||
|
layouts = ValidLayoutParams;
|
||||||
referenceData: ReferenceEventRequest;
|
referenceData: ReferenceEventRequest;
|
||||||
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent;
|
@ViewChild("stepper", { static: false }) verticalStepper: VerticalStepperComponent;
|
||||||
|
|
||||||
|
@ -87,39 +122,38 @@ export class TrialInitiationComponent implements OnInit, OnDestroy {
|
||||||
|
|
||||||
this.referenceDataId = qParams.reference;
|
this.referenceDataId = qParams.reference;
|
||||||
|
|
||||||
if (!qParams.org) {
|
if (Object.values(ValidLayoutParams).includes(qParams.layout)) {
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (qParams.layout) {
|
|
||||||
this.layout = qParams.layout;
|
this.layout = qParams.layout;
|
||||||
|
this.accountCreateOnly = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.validOrgs.includes(qParams.org)) {
|
if (this.trialFlowOrgs.includes(qParams.org)) {
|
||||||
this.org = qParams.org;
|
this.org = qParams.org;
|
||||||
} else {
|
this.orgLabel = this.titleCasePipe.transform(this.org);
|
||||||
this.org = "families";
|
this.useTrialStepper = true;
|
||||||
}
|
|
||||||
|
|
||||||
this.referenceData.flow = qParams.org;
|
this.referenceData.flow = qParams.org;
|
||||||
|
|
||||||
|
if (this.org === ValidOrgParams.families) {
|
||||||
|
this.plan = PlanType.FamiliesAnnually;
|
||||||
|
this.product = ProductType.Families;
|
||||||
|
} else if (this.org === ValidOrgParams.teams) {
|
||||||
|
this.plan = PlanType.TeamsAnnually;
|
||||||
|
this.product = ProductType.Teams;
|
||||||
|
} else if (this.org === ValidOrgParams.enterprise) {
|
||||||
|
this.plan = PlanType.EnterpriseAnnually;
|
||||||
|
this.product = ProductType.Enterprise;
|
||||||
|
}
|
||||||
|
} else if (this.routeFlowOrgs.includes(qParams.org)) {
|
||||||
|
this.referenceData.flow = qParams.org;
|
||||||
|
const route = this.router.createUrlTree(["create-organization"], {
|
||||||
|
queryParams: { plan: qParams.org },
|
||||||
|
});
|
||||||
|
this.routerService.setPreviousUrl(route.toString());
|
||||||
|
}
|
||||||
|
|
||||||
// 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);
|
||||||
|
|
||||||
this.orgLabel = this.titleCasePipe.transform(this.org);
|
|
||||||
this.accountCreateOnly = false;
|
|
||||||
|
|
||||||
if (this.org === "families") {
|
|
||||||
this.plan = PlanType.FamiliesAnnually;
|
|
||||||
this.product = ProductType.Families;
|
|
||||||
} else if (this.org === "teams") {
|
|
||||||
this.plan = PlanType.TeamsAnnually;
|
|
||||||
this.product = ProductType.Teams;
|
|
||||||
} else if (this.org === "enterprise") {
|
|
||||||
this.plan = PlanType.EnterpriseAnnually;
|
|
||||||
this.product = ProductType.Enterprise;
|
|
||||||
}
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const invite = await this.stateService.getOrganizationInvitation();
|
const invite = await this.stateService.getOrganizationInvitation();
|
||||||
|
|
|
@ -15,7 +15,6 @@ import { LoginWithDeviceComponent } from "./accounts/login/login-with-device.com
|
||||||
import { LoginComponent } from "./accounts/login/login.component";
|
import { LoginComponent } from "./accounts/login/login.component";
|
||||||
import { RecoverDeleteComponent } from "./accounts/recover-delete.component";
|
import { RecoverDeleteComponent } from "./accounts/recover-delete.component";
|
||||||
import { RecoverTwoFactorComponent } from "./accounts/recover-two-factor.component";
|
import { RecoverTwoFactorComponent } from "./accounts/recover-two-factor.component";
|
||||||
import { RegisterComponent } from "./accounts/register.component";
|
|
||||||
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
import { RemovePasswordComponent } from "./accounts/remove-password.component";
|
||||||
import { SetPasswordComponent } from "./accounts/set-password.component";
|
import { SetPasswordComponent } from "./accounts/set-password.component";
|
||||||
import { SsoComponent } from "./accounts/sso.component";
|
import { SsoComponent } from "./accounts/sso.component";
|
||||||
|
@ -69,16 +68,15 @@ const routes: Routes = [
|
||||||
{ path: "2fa", component: TwoFactorComponent, canActivate: [UnauthGuard] },
|
{ path: "2fa", component: TwoFactorComponent, canActivate: [UnauthGuard] },
|
||||||
{
|
{
|
||||||
path: "register",
|
path: "register",
|
||||||
component: RegisterComponent,
|
component: TrialInitiationComponent,
|
||||||
canActivate: [UnauthGuard],
|
canActivate: [UnauthGuard],
|
||||||
data: { titleId: "createAccount" },
|
data: { titleId: "createAccount" },
|
||||||
},
|
},
|
||||||
buildFlaggedRoute("showTrial", {
|
{
|
||||||
path: "trial",
|
path: "trial",
|
||||||
component: TrialInitiationComponent,
|
redirectTo: "register",
|
||||||
canActivate: [UnauthGuard],
|
pathMatch: "full",
|
||||||
data: { titleId: "startTrial" },
|
},
|
||||||
}),
|
|
||||||
{
|
{
|
||||||
path: "sso",
|
path: "sso",
|
||||||
component: SsoComponent,
|
component: SsoComponent,
|
||||||
|
|
|
@ -7,7 +7,6 @@ import { LockComponent } from "../accounts/lock.component";
|
||||||
import { RecoverDeleteComponent } from "../accounts/recover-delete.component";
|
import { RecoverDeleteComponent } from "../accounts/recover-delete.component";
|
||||||
import { RecoverTwoFactorComponent } from "../accounts/recover-two-factor.component";
|
import { RecoverTwoFactorComponent } from "../accounts/recover-two-factor.component";
|
||||||
import { RegisterFormModule } from "../accounts/register-form/register-form.module";
|
import { RegisterFormModule } from "../accounts/register-form/register-form.module";
|
||||||
import { RegisterComponent } from "../accounts/register.component";
|
|
||||||
import { RemovePasswordComponent } from "../accounts/remove-password.component";
|
import { RemovePasswordComponent } from "../accounts/remove-password.component";
|
||||||
import { SetPasswordComponent } from "../accounts/set-password.component";
|
import { SetPasswordComponent } from "../accounts/set-password.component";
|
||||||
import { SsoComponent } from "../accounts/sso.component";
|
import { SsoComponent } from "../accounts/sso.component";
|
||||||
|
@ -218,7 +217,6 @@ import { SharedModule } from ".";
|
||||||
PurgeVaultComponent,
|
PurgeVaultComponent,
|
||||||
RecoverDeleteComponent,
|
RecoverDeleteComponent,
|
||||||
RecoverTwoFactorComponent,
|
RecoverTwoFactorComponent,
|
||||||
RegisterComponent,
|
|
||||||
RemovePasswordComponent,
|
RemovePasswordComponent,
|
||||||
SecurityComponent,
|
SecurityComponent,
|
||||||
SecurityKeysComponent,
|
SecurityKeysComponent,
|
||||||
|
@ -341,7 +339,6 @@ import { SharedModule } from ".";
|
||||||
PurgeVaultComponent,
|
PurgeVaultComponent,
|
||||||
RecoverDeleteComponent,
|
RecoverDeleteComponent,
|
||||||
RecoverTwoFactorComponent,
|
RecoverTwoFactorComponent,
|
||||||
RegisterComponent,
|
|
||||||
RemovePasswordComponent,
|
RemovePasswordComponent,
|
||||||
SecurityComponent,
|
SecurityComponent,
|
||||||
SecurityKeysComponent,
|
SecurityKeysComponent,
|
||||||
|
|
|
@ -9,7 +9,6 @@ import {
|
||||||
// required to avoid linting errors when there are no flags
|
// required to avoid linting errors when there are no flags
|
||||||
/* eslint-disable-next-line @typescript-eslint/ban-types */
|
/* eslint-disable-next-line @typescript-eslint/ban-types */
|
||||||
export type Flags = {
|
export type Flags = {
|
||||||
showTrial?: boolean;
|
|
||||||
secretsManager?: boolean;
|
secretsManager?: boolean;
|
||||||
showPasswordless?: boolean;
|
showPasswordless?: boolean;
|
||||||
} & SharedFlags;
|
} & SharedFlags;
|
||||||
|
|
Loading…
Reference in New Issue