Website: Add testimonials to /register page (#20394)

Closes: #20074

Changes:
- Added personalized testimonials to the /register page.
This commit is contained in:
Eric 2024-07-12 17:00:27 -05:00 committed by GitHub
parent 36ca428b17
commit be919835e2
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 232 additions and 67 deletions

View file

@ -27,6 +27,7 @@ parasails.registerPage('signup', {
// For redirecting users coming from the "Get your license" link to the license dispenser.
loginSlug: '/login',
pageToRedirectToAfterRegistration: '/start',
primaryBuyingSituation: undefined
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗

View file

@ -1,34 +1,74 @@
#signup {
padding-top: 80px;
h1 {
font-size: 28px;
line-height: 38px;
font-size: 32px;
line-height: 120%;
}
a {
line-height: 150%;
color: @core-fleet-black-75;
text-decoration: underline;
text-underline-offset: 2px;
}
[purpose='page-heading'] {
padding-left: 30px;
padding-right: 30px;
text-align: center;
margin-bottom: 40px;
p {
line-height: 150%;
}
[purpose='page-container'] {
padding: 64px 128px 64px 128px;
max-width: 1200px;
}
[purpose='login-link'] {
margin-bottom: 4px;
a {
float: right;
[purpose='page-heading'] {
text-align: left;
margin-bottom: 48px;
}
[purpose='quote-and-logos'] {
max-width: 310px;
}
[purpose='quote'] {
margin-top: 8px;
}
[purpose='quote-text'] {
font-size: 14px;
line-height: 150%;
font-style: italic;
}
[purpose='quote-author-info'] {
display: inline-flex;
padding: 4px 16px 4px 4px;
border-radius: 28px;
width: fit-content;
margin-top: 8px;
margin-bottom: 48px;
[purpose='job-title'] {
color: @core-fleet-black-75;
text-decoration: underline;
font-size: 14px;
font-size: 12px;
font-weight: 400;
line-height: 18px;
margin-bottom: 0px;
}
[purpose='name'] {
font-size: 12px;
font-weight: 700;
line-height: 18px;
margin-bottom: 0px;
}
[purpose='profile-picture'] {
margin-right: 16px;
img {
height: 32px;
width: 32px;
}
}
}
[purpose='logos'] {
max-width: 310px;
}
[purpose='signup-form'] {
width: 528px;
}
[purpose='customer-portal-form'] {
max-width: 560px;
border-radius: 16px;
margin-bottom: 40px;
padding: 20px 32px 32px 32px;
label {
color: @core-fleet-black;
@ -57,6 +97,19 @@
appearance: none;
-webkit-appearance: none;
}
.small {
font-size: 12px;
}
}
[purpose='login-link'] {
margin-bottom: 4px;
a {
float: right;
color: @core-fleet-black-75;
text-decoration: underline;
font-size: 14px;
}
}
[purpose='submit-button'] {
@ -78,21 +131,61 @@
font-weight: 700;
}
}
@media (max-width: 1200px) {
[purpose='page-container'] {
padding: 64px;
}
}
@media (max-width: 991px) {
[purpose='signup-form'] {
max-width: 528px;
}
[purpose='quote-and-logos'] {
max-width: 528px;
margin-top: 48px;
}
[purpose='logos'] {
max-width: 528px;
}
[purpose='page-heading'] {
text-align: center;
margin-left: auto;
margin-right: auto;
margin-bottom: 48px;
max-width: 528px;
}
[purpose='page-container'] {
padding: 64px 32px;
}
}
@media (max-width: 768px) {
padding-top: 60px;
[purpose='customer-portal-form'] {
max-width: unset;
}
[purpose='signup-form'] {
width: 100%;
}
[purpose='quote-and-logos'] {
width: 100%;
}
[purpose='logos'] {
width: 100%;
}
}
@media (max-width: 576px) {
padding-top: 40px;
[purpose='page-heading'] {
padding-left: 0px;
padding-right: 0px;
}
[purpose='page-container'] {
padding: 48px 24px;
}
[purpose='login-link'] {
margin-bottom: 12px;
}
[purpose='customer-portal-form'] {
.card-body {
padding: 1.5em 1em;

View file

@ -113,7 +113,6 @@ module.exports.routes = {
'GET /register': {
action: 'entrance/view-signup',
locals: {
hideHeaderLinks: true,
hideFooterLinks: true,
pageTitleForMeta: 'Sign up | Fleet',
pageDescriptionForMeta: 'Sign up for a Fleet account.',
@ -122,7 +121,6 @@ module.exports.routes = {
'GET /login': {
action: 'entrance/view-login',
locals: {
hideHeaderLinks: true,
hideFooterLinks: true,
pageTitleForMeta: 'Log in | Fleet',
pageDescriptionForMeta: 'Log in to Fleet.',

View file

@ -1,62 +1,135 @@
<div id="signup" v-cloak>
<div style="max-width: 560px;" class="container-fluid pb-5 px-lg-0 px-3">
<div purpose="page-container" class="container">
<div purpose="page-heading">
<h1>Welcome to Fleet</h1>
<p class="mb-0">We just need a few details in order to get started.</p>
</div>
<div purpose="customer-portal-form" class="card card-body">
<div purpose="login-link">
<a :href="loginSlug">I have an account</a>
<div purpose="form-container" class="d-flex flex-lg-row flex-column justify-content-between align-items-start">
<div purpose="signup-form" class="mx-auto mx-lg-0">
<div purpose="customer-portal-form" class="card card-body">
<div purpose="login-link">
<a :href="loginSlug">I have an account</a>
</div>
<ajax-form action="signup" class="self-service-register" :syncing.sync="syncing" :cloud-error.sync="cloudError" :form-errors.sync="formErrors" :form-data="formData" :form-rules="formRules" @submitted="submittedSignUpForm()">
<div class="form-group">
<label for="email-address">Work email *</label>
<input class="form-control" id="email-address" :class="[formErrors.emailAddress ? 'is-invalid' : '']" v-model.trim="formData.emailAddress" @input="typeClearOneFormError('emailAddress')">
<div class="invalid-feedback" v-if="formErrors.emailAddress" focus-first>This doesnt appear to be a valid email address</div>
</div>
<div v-show="formData.emailAddress || showFullForm">
<div class="form-group">
<label for="password">Choose a password *</label>
<input class="form-control" id="password" type="password" :class="[formErrors.password ? 'is-invalid' : '']" v-model.trim="formData.password" autocomplete="new-password" @input="typeClearOneFormError('password')">
<div class="invalid-feedback" v-if="formErrors.password === 'minLength'">Password too short.</div>
<div class="invalid-feedback" v-if="formErrors.password === 'required'">Please enter a password.</div>
<p class="mt-2 small"> Minimum length is 8 characters</p>
</div>
<div class="form-group">
<label for="organization">Organization *</label>
<input class="form-control" id="organization" type="text" :class="[formErrors.organization ? 'is-invalid' : '']" v-model.trim="formData.organization" @input="typeClearOneFormError('organization')">
<div class="invalid-feedback" v-if="formErrors.organization">Please enter the name of your organization.</div>
</div>
<div class="row">
<div class="col-12 col-sm-6 pr-sm-2">
<div class="form-group">
<label for="first-name">First name *</label>
<input class="form-control" id="first-name" type="text" :class="[formErrors.firstName ? 'is-invalid' : '']" v-model.trim="formData.firstName" autocomplete="first-name" @input="typeClearOneFormError('firstName')">
<div class="invalid-feedback" v-if="formErrors.firstName">Please enter your first name.</div>
</div>
</div>
<div class="col-12 col-sm-6 pl-sm-2">
<div class="form-group">
<label for="last-name">Last name *</label>
<input class="form-control" id="last-name" type="text" :class="[formErrors.lastName ? 'is-invalid' : '']" v-model.trim="formData.lastName" autocomplete="last-name" @input="typeClearOneFormError('lastName')">
<div class="invalid-feedback" v-if="formErrors.lastName">Please enter your last name.</div>
</div>
</div>
</div>
</div>
<cloud-error v-if="cloudError==='emailAlreadyInUse'">
<p>This email is already linked to a Fleet account.<br> Please <a href="/login">sign in</a> with your email and password.</p>
</cloud-error>
<cloud-error v-else-if="cloudError === 'invalidEmailDomain'">
<p>Please enter your work or school email address</p>
</cloud-error>
<cloud-error purpose="cloud-error" v-else-if="cloudError"></cloud-error>
<p class="small">By signing up you agree to our <a href="/legal/privacy">privacy policy</a> and <a href="/terms">terms of service</a>.</p>
<ajax-button purpose="submit-button" spinner="true" type="submit" :syncing="syncing" class="btn btn-block btn-lg btn-primary mt-4" v-if="!cloudError">Agree and continue</ajax-button>
<ajax-button purpose="submit-button" type="button" :syncing="syncing" class="btn btn-block btn-lg btn-primary mt-4" v-if="cloudError" @click="clickResetForm()">Try again</ajax-button>
</ajax-form>
</div>
</div>
<ajax-form action="signup" class="self-service-register" :syncing.sync="syncing" :cloud-error.sync="cloudError" :form-errors.sync="formErrors" :form-data="formData" :form-rules="formRules" @submitted="submittedSignUpForm()">
<div class="form-group">
<label for="email-address">Work email *</label>
<input class="form-control" id="email-address" :class="[formErrors.emailAddress ? 'is-invalid' : '']" v-model.trim="formData.emailAddress" @input="typeClearOneFormError('emailAddress')">
<div class="invalid-feedback" v-if="formErrors.emailAddress" focus-first>This doesnt appear to be a valid email address</div>
</div>
<div v-show="formData.emailAddress || showFullForm">
<div class="form-group">
<label for="password">Choose a password *</label>
<input class="form-control" id="password" type="password" :class="[formErrors.password ? 'is-invalid' : '']" v-model.trim="formData.password" autocomplete="new-password" @input="typeClearOneFormError('password')">
<div class="invalid-feedback" v-if="formErrors.password === 'minLength'">Password too short.</div>
<div class="invalid-feedback" v-if="formErrors.password === 'required'">Please enter a password.</div>
<p class="mt-2"> Minimum length is 8 characters</p>
</div>
<div class="form-group">
<label for="organization">Organization *</label>
<input class="form-control" id="organization" type="text" :class="[formErrors.organization ? 'is-invalid' : '']" v-model.trim="formData.organization" @input="typeClearOneFormError('organization')">
<div class="invalid-feedback" v-if="formErrors.organization">Please enter the name of your organization.</div>
</div>
<div class="row">
<div class="col-12 col-sm-6 pr-sm-2">
<div class="form-group">
<label for="first-name">First name *</label>
<input class="form-control" id="first-name" type="text" :class="[formErrors.firstName ? 'is-invalid' : '']" v-model.trim="formData.firstName" autocomplete="first-name" @input="typeClearOneFormError('firstName')">
<div class="invalid-feedback" v-if="formErrors.firstName">Please enter your first name.</div>
<div purpose="quote-and-logos" class="mx-auto mx-lg-0">
<% if(typeof primaryBuyingSituation === 'undefined' || ['mdm'].includes(primaryBuyingSituation)) { %>
<div purpose="quote">
<img alt="an opening quotation mark" style="width:20px; margin-bottom: 16px;" src="/images/icon-quote-21x17@2x.png">
<p purpose="quote-text">
Exciting. This is a team that listens to feedback.
</p>
<div purpose="quote-author-info" class="d-flex flex-row align-items-center">
<div purpose="profile-picture">
<img alt="Erik Gomez" src="/images/testimonial-author-erik-gomez-48x48@2x.png">
</div>
</div>
<div class="col-12 col-sm-6 pl-sm-2">
<div class="form-group">
<label for="last-name">Last name *</label>
<input class="form-control" id="last-name" type="text" :class="[formErrors.lastName ? 'is-invalid' : '']" v-model.trim="formData.lastName" autocomplete="last-name" @input="typeClearOneFormError('lastName')">
<div class="invalid-feedback" v-if="formErrors.lastName">Please enter your last name.</div>
<div class="d-flex flex-column align-self-top">
<p purpose="name" class="font-weight-bold m-0">Erik Gomez</p>
<p purpose="job-title" class="m-0">Staff Client Platform Engineer</p>
</div>
</div>
</div>
<% } else if (['eo-it'].includes(primaryBuyingSituation)) { %>
<div purpose="quote">
<img alt="an opening quotation mark" style="width:20px; margin-bottom: 16px;" src="/images/icon-quote-21x17@2x.png">
<p purpose="quote-text">
When we look at vendors, we look for ones that are very receptive to feedback, where youre just part of the family, I guess. Fleets really good at that.
</p>
<div purpose="quote-author-info" class="d-flex flex-row align-items-center">
<div purpose="profile-picture">
<img alt="Harrison Ravazzolo" src="/images/testimonial-author-harrison-ravazzolo-48x48@2x.png">
</div>
<div class="d-flex flex-column align-self-top">
<p purpose="name" class="font-weight-bold m-0">Harrison Ravazzolo</p>
<p purpose="job-title" class="m-0">Lead platform and identity engineer</p>
</div>
</div>
</div>
<% } else if (['eo-security'].includes(primaryBuyingSituation)) { %>
<div purpose="quote">
<img alt="an opening quotation mark" style="width:20px; margin-bottom: 16px;" src="/images/icon-quote-21x17@2x.png">
<p purpose="quote-text">
Something I really appreciate about working with you guys is that it doesn't feel like I'm talking to a vendor. It actually feels like I'm talking to my team, and I really appreciate it.
</p>
<div purpose="quote-author-info" class="d-flex flex-row align-items-center">
<div purpose="profile-picture">
<img alt="Chandra Majumdar" src="/images/testimonial-author-chandra-majumdar-48x48@2x.png">
</div>
<div class="d-flex flex-column align-self-top">
<p purpose="name" class="font-weight-bold m-0">Chandra Majumdar</p>
<p purpose="job-title" class="m-0">Partner - Cyber and Strategic Risk</p>
</div>
</div>
</div>
<% } else if (['vm'].includes(primaryBuyingSituation)) { %>
<div purpose="quote">
<img alt="an opening quotation mark" style="width:20px; margin-bottom: 16px;" src="/images/icon-quote-21x17@2x.png">
<p purpose="quote-text">
The visibility down into the assets covered by the agent is phenomenal. Fleet has become the central source for a lot of things.
</p>
<div purpose="quote-author-info" class="d-flex flex-row align-items-center">
<div purpose="profile-picture">
<img alt="Andre Shields" src="/images/testimonial-author-andre-shields-48x48@2x.png">
</div>
<div class="d-flex flex-column align-self-top">
<p purpose="name" class="font-weight-bold m-0">Andre Shields</p>
<p purpose="job-title" class="m-0">Staff Cybersecurity Engineer, Vulnerability Management</p>
</div>
</div>
</div>
<% } %>
<div purpose="logos" class="flex-column flex-wrap align-items-center w-100">
<logo-carousel></logo-carousel>
</div>
<cloud-error v-if="cloudError==='emailAlreadyInUse'">
<p>This email is already linked to a Fleet account.<br> Please <a href="/login">sign in</a> with your email and password.</p>
</cloud-error>
<cloud-error v-else-if="cloudError === 'invalidEmailDomain'">
<p>Please enter your work or school email address</p>
</cloud-error>
<cloud-error purpose="cloud-error" v-else-if="cloudError"></cloud-error>
<p class="small">By signing up you agree to our <a href="/legal/privacy">privacy policy</a> and <a href="/terms">terms of service</a>.</p>
<ajax-button purpose="submit-button" spinner="true" type="submit" :syncing="syncing" class="btn btn-block btn-lg btn-primary mt-4" v-if="!cloudError">Agree and continue</ajax-button>
<ajax-button purpose="submit-button" type="button" :syncing="syncing" class="btn btn-block btn-lg btn-primary mt-4" v-if="cloudError" @click="clickResetForm()">Try again</ajax-button>
</ajax-form>
</div>
</div>
<logo-carousel></logo-carousel>
</div>
</div>
<%- /* Expose locals as `window.SAILS_LOCALS` :: */ exposeLocalsToBrowser() %>