diff --git a/apps/web/src/app/oss-routing.module.ts b/apps/web/src/app/oss-routing.module.ts index 066ed5db10..29983e3d27 100644 --- a/apps/web/src/app/oss-routing.module.ts +++ b/apps/web/src/app/oss-routing.module.ts @@ -8,6 +8,7 @@ import { tdeDecryptionRequiredGuard, UnauthGuard, } from "@bitwarden/angular/auth/guards"; +import { RegistrationStartComponent } from "@bitwarden/auth/angular"; import { flagEnabled, Flags } from "../utils/flags"; @@ -17,6 +18,7 @@ import { VerifyRecoverDeleteProviderComponent } from "./admin-console/providers/ import { CreateOrganizationComponent } from "./admin-console/settings/create-organization.component"; import { SponsoredFamiliesComponent } from "./admin-console/settings/sponsored-families.component"; import { AcceptOrganizationComponent } from "./auth/accept-organization.component"; +import { AnonLayoutWrapperComponent } from "./auth/anon-layout-wrapper.component"; import { deepLinkGuard } from "./auth/guards/deep-link.guard"; import { HintComponent } from "./auth/hint.component"; import { LockComponent } from "./auth/lock.component"; @@ -290,6 +292,18 @@ const routes: Routes = [ { path: "setup/families-for-enterprise", component: FamiliesForEnterpriseSetupComponent }, ], }, + { + path: "", + component: AnonLayoutWrapperComponent, + children: [ + { + path: "start-registration", + component: RegistrationStartComponent, + canActivate: [], + data: { pageTitle: "createAccount" }, + }, + ], + }, { path: "organizations", loadChildren: () => diff --git a/libs/auth/src/angular/index.ts b/libs/auth/src/angular/index.ts index 067ed63b8e..0440e6a3d9 100644 --- a/libs/auth/src/angular/index.ts +++ b/libs/auth/src/angular/index.ts @@ -13,3 +13,6 @@ export * from "./password-callout/password-callout.component"; export * from "./user-verification/user-verification-dialog.component"; export * from "./user-verification/user-verification-dialog.types"; export * from "./user-verification/user-verification-form-input.component"; + +// registration +export * from "./registration/registration-start/registration-start.component"; diff --git a/libs/auth/src/angular/registration/registration-start/registration-start.component.html b/libs/auth/src/angular/registration/registration-start/registration-start.component.html new file mode 100644 index 0000000000..a0dec38e20 --- /dev/null +++ b/libs/auth/src/angular/registration/registration-start/registration-start.component.html @@ -0,0 +1,38 @@ +
+
+ + {{ "emailAddress" | i18n }} + + + + + {{ "name" | i18n }} + + + + + + + {{ "acceptPolicies" | i18n }}
+ {{ + "termsOfService" | i18n + }}, + {{ + "privacyPolicy" | i18n + }} +
+
+
+
diff --git a/libs/auth/src/angular/registration/registration-start/registration-start.component.ts b/libs/auth/src/angular/registration/registration-start/registration-start.component.ts new file mode 100644 index 0000000000..771002e2dc --- /dev/null +++ b/libs/auth/src/angular/registration/registration-start/registration-start.component.ts @@ -0,0 +1,87 @@ +import { CommonModule } from "@angular/common"; +import { Component, OnDestroy, OnInit } from "@angular/core"; +import { + AbstractControl, + FormBuilder, + FormControl, + ReactiveFormsModule, + ValidatorFn, + Validators, +} from "@angular/forms"; +import { ActivatedRoute } from "@angular/router"; +import { Subject, takeUntil } from "rxjs"; + +import { JslibModule } from "@bitwarden/angular/jslib.module"; +import { PlatformUtilsService } from "@bitwarden/common/platform/abstractions/platform-utils.service"; +import { ButtonModule, CheckboxModule, FormFieldModule } from "@bitwarden/components"; + +@Component({ + standalone: true, + selector: "auth-registration-start", + templateUrl: "./registration-start.component.html", + imports: [ + CommonModule, + ReactiveFormsModule, + JslibModule, + FormFieldModule, + CheckboxModule, + ButtonModule, + ], +}) +export class RegistrationStartComponent implements OnInit, OnDestroy { + queryParamFromOrgInvite: boolean = false; + + showTerms = true; + + formGroup = this.formBuilder.group({ + email: ["", [Validators.required, Validators.email]], + name: [""], + acceptPolicies: [false, [this.acceptPoliciesValidator()]], + }); + + get email(): FormControl { + return this.formGroup.get("email") as FormControl; + } + + get name(): FormControl { + return this.formGroup.get("name") as FormControl; + } + + private destroy$ = new Subject(); + + constructor( + private formBuilder: FormBuilder, + private route: ActivatedRoute, + private platformUtilsService: PlatformUtilsService, + ) { + this.showTerms = !platformUtilsService.isSelfHost(); + } + + async ngOnInit() { + this.listenForQueryParamChanges(); + } + + submit = async () => {}; + + private listenForQueryParamChanges() { + this.route.queryParams.pipe(takeUntil(this.destroy$)).subscribe((qParams) => { + if (qParams.email != null && qParams.email.indexOf("@") > -1) { + this.email?.setValue(qParams.email); + this.queryParamFromOrgInvite = qParams.fromOrgInvite === "true"; + } + }); + } + + private acceptPoliciesValidator(): ValidatorFn { + return (control: AbstractControl) => { + const ctrlValue = control.value; + + return !ctrlValue && this.showTerms ? { required: true } : null; + }; + } + + ngOnDestroy(): void { + this.destroy$.next(); + this.destroy$.complete(); + } +}