Website: add newsletter subscription form to homepage (#23362)

Related to: #23212

Changes:
- Added a form to the homepage for users to subscribe to the Fleet
newsletter.
- Updated the unsubscribe-from-all-newsletters action to redirect users
to the fleet homepage once they are unsubscribed from the newsletter.
- Updated the update-or-create-one-newsletter-subscription action to
create/update CRM records when users sign up for the Fleet newsletter.
This commit is contained in:
Eric
2024-10-30 15:14:08 +09:00
committed by GitHub
parent 4cc30f5fd1
commit ca73acfc3a
8 changed files with 183 additions and 25 deletions
@@ -13,12 +13,6 @@ module.exports = {
required: true,
},
subscribeTo: {
type: 'string',
required: true,
description: 'The type of content that this user is changing their subscription for',
isIn: ['releases']
}
},
@@ -28,24 +22,39 @@ module.exports = {
description: 'A user has successfully changed their subscription to the Fleet newsletter'
},
invalidEmailDomain: {
description: 'This email address is on a denylist of domains and was not delivered.',
responseType: 'badRequest'
},
},
fn: async function ({emailAddress, subscribeTo}) {
fn: async function ({emailAddress}) {
let emailDomain = emailAddress.split('@')[1];
if(_.includes(sails.config.custom.bannedEmailDomainsForWebsiteSubmissions, emailDomain.toLowerCase())){
throw 'invalidEmailDomain';
}
await NewsletterSubscription.create({emailAddress: emailAddress})
.tolerate('E_UNIQUE');
let argins = {};
await NewsletterSubscription.updateOne({emailAddress: emailAddress}).set({isSubscribedToReleases: true});
// Update the NewsletterSubscription record for this email address with `isSubscribedTo____` boolean attributes based on the subscribeTo input
if(subscribeTo === 'releases') {
argins.isSubscribedToReleases = true;
}
// FUTURE: Handle more types of subscribeTo inputs
await NewsletterSubscription.updateOne({emailAddress: emailAddress}).set(argins);
sails.helpers.salesforce.updateOrCreateContactAndAccount.with({
emailAddress: emailAddress,
contactSource: 'Website - Newsletter',
description: `Subscribed to the Fleet newsletter`,
psychologicalStage: '3 - Intrigued',
psychologicalStageChangeReason: 'Website - Newsletter',
}).exec((err)=>{// Use .exec() to run the salesforce helpers in the background.
if(err) {
sails.log.warn(`Background task failed: When a user signed up for a newsletter, a lead/contact could not be updated in the CRM for this email address: ${emailAddress}.`, err);
}
return;
});
// All done.
return;
@@ -35,7 +35,7 @@ module.exports = {
sails.log.warn('When a user tried to unsubscribe from the Fleet newsletter, a NewsletterSubscription record for the specified email address ('+ emailAddress +') could not be found.');
}
// All done.
return;
return this.res.redirect('/#unsubscribed');
}
@@ -14,8 +14,8 @@ module.exports = {
linkedinUrl: { type: 'string' },
// Set…
firstName: { type: 'string', required: true },
lastName: { type: 'string', required: true },
firstName: { type: 'string'},
lastName: { type: 'string'},
organization: { type: 'string' },
description: { type: 'string' },
primaryBuyingSituation: { type: 'string' },
@@ -39,6 +39,7 @@ module.exports = {
isIn: [
'Website - Contact forms',
'Website - Sign up',
'Website - Newsletter',
],
},
getStartedResponses: {
@@ -237,8 +238,8 @@ module.exports = {
.create({
AccountId: salesforceAccountId,
OwnerId: salesforceAccountOwnerId,
FirstName: firstName,
LastName: lastName,
FirstName: firstName ? firstName : '?',
LastName: lastName ? lastName : '?',
...valuesToSet,
});
// console.log(`Created ${newContactRecord.id}`);
+22 -2
View File
@@ -4,7 +4,20 @@ parasails.registerPage('homepage', {
// ╩╝╚╝╩ ╩ ╩╩ ╩╩═╝ ╚═╝ ╩ ╩ ╩ ╩ ╚═╝
data: {
modal: undefined,
selectedCategory: 'mdm'
selectedCategory: 'mdm',
formData: { /* … */ },
formErrors: { /* … */ },
// Form rules
formRules: {
emailAddress: {isEmail: true, required: true},
},
syncing: false,
// Server error state for the form
cloudError: '',
cloudSuccess: false,
},
// ╦ ╦╔═╗╔═╗╔═╗╦ ╦╔═╗╦ ╔═╗
@@ -33,6 +46,13 @@ parasails.registerPage('homepage', {
closeModal: function() {
this.modal = undefined;
},
submittedNewsletterForm: async function() {
// Show the success message.
this.cloudSuccess = true;
this.formData = {};
await setTimeout(()=>{
this.cloudSuccess = false;
}, 10000);
},
}
});
+110 -2
View File
@@ -708,7 +708,61 @@
}
}
}
[purpose='newsletter-banner'] {
width: 660px;
margin-left: auto;
margin-right: auto;
margin-top: 32px;
margin-bottom: 32px;
border-radius: 8px;
border: 1px solid #E2E4EA;
background: #F9FAFC;
display: block;
padding: 8px 8px 8px 16px;
input {
border-radius: 6px;
border: 1px solid var(--ui-fleet-blue-15, #E2E4EA);
background: var(--Core-White, #FFF);
display: flex;
width: 235px;
padding: 8px 15px;
align-items: center;
flex-shrink: 0;
align-self: stretch;
height: 37px;
margin-right: 10px;
}
p {
color: #515774;
margin-bottom: 0px;
font-size: 14px;
font-weight: 400;
line-height: 21px;
}
[purpose='subscribe-button'] {
color: #FFF;
text-align: center;
display: flex;
padding: 16px;
justify-content: center;
align-items: center;
gap: 4px;
align-self: stretch;
border-radius: 4px;
background: #FF5C83;
height: 37px;
width: 92px;
/* Body XS (bold) */
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 700;
line-height: 18px; /* 150% */
}
[purpose='newsletter-form-input']::placeholder {
font-size: 14px;
}
}
[purpose='video-modal'] {
[purpose='modal-dialog'] {
width: 100%;
@@ -1229,7 +1283,61 @@
}
}
}
[purpose='newsletter-banner'] {
width: 100%;
margin-left: auto;
margin-right: auto;
border-radius: 8px;
border: 1px solid #E2E4EA;
background: #F9FAFC;
display: block;
padding: 16px;
margin-top: 24px;
margin-bottom: 24px;
input {
border-radius: 6px;
border: 1px solid #E2E4EA;
background: #FFF;
display: flex;
width: 100%;
padding: 8px 15px;
align-items: center;
flex-shrink: 0;
align-self: stretch;
height: 37px;
margin-right: 0px;
margin-bottom: 10px;
}
p {
color: #515774;
margin-bottom: 8px;
font-size: 14px;
font-weight: 400;
line-height: 21px;
}
[purpose='subscribe-button'] {
color: #FFF;
text-align: center;
display: flex;
padding: 16px;
justify-content: center;
align-items: center;
gap: 4px;
align-self: stretch;
border-radius: 4px;
background: #FF5C83;
height: 37px;
width: 100%;
font-family: Inter;
font-size: 12px;
font-style: normal;
font-weight: 700;
line-height: 18px; /* 150% */
}
[purpose='newsletter-form-input']::placeholder {
font-size: 14px;
}
}
[purpose='video-modal'] {
[purpose='modal-dialog'] {
max-width: 97vw;
+1
View File
@@ -518,6 +518,7 @@ module.exports.routes = {
'GET /g': (req,res)=> { let originalQueryStringWithAmp = req.url.match(/\?(.+)$/) ? '&'+req.url.match(/\?(.+)$/)[1] : ''; return res.redirect(301, sails.config.custom.baseUrl+'/?meet-fleet'+originalQueryStringWithAmp); },
'GET /test-fleet-sandbox': '/register',
'GET /unsubscribe': (req,res)=> { let originalQueryString = req.url.match(/\?(.+)$/) ? req.url.match(/\?(.+)$/)[1] : ''; return res.redirect(301, sails.config.custom.baseUrl+'/api/v1/unsubscribe-from-marketing-emails?'+originalQueryString);},
'GET /unsubscribe-from-newsletter': (req,res)=> { let originalQueryString = req.url.match(/\?(.+)$/) ? req.url.match(/\?(.+)$/)[1] : ''; return res.redirect(301, sails.config.custom.baseUrl+'/api/v1/unsubscribe-from-all-newsletters?'+originalQueryString);},
'GET /tables': '/tables/account_policy_data',
'GET /imagine/launch-party': 'https://www.eventbrite.com/e/601763519887',
'GET /blackhat2023': 'https://github.com/fleetdm/fleet/tree/main/tools/blackhat-mdm', // Assets from @marcosd4h & @zwass Black Hat 2023 talk
+1 -1
View File
@@ -15,7 +15,7 @@
<p>© <%= (new Date()).getFullYear() %> Fleet Device Management Inc. All trademarks, service marks, and company names are the property of their respective owners.</p>
</div>
<div>
<p style="font-size: 12px; color: #3E4771;">You received this email because youre subscribed to the Fleet newsletter. <a href="<%= url.resolve(sails.config.custom.baseUrl, '/unsubscribe?emailAddress='+encodeURIComponent(emailAddress)) %>">Unsubscribe</a></p>
<p style="font-size: 12px; color: #3E4771;">You received this email because youre subscribed to the Fleet newsletter. <a href="<%= url.resolve(sails.config.custom.baseUrl, '/unsubscribe-from-newsletter?emailAddress='+encodeURIComponent(emailAddress)) %>">Unsubscribe</a></p>
</div>
</div>
</div>
+19
View File
@@ -405,6 +405,25 @@
</div>
<%/* Cloud city banner */%>
<parallax-city></parallax-city>
<div purpose="homepage-content" class="container py-4 my-0 my-md-2">
<div purpose="newsletter-banner">
<div class="d-flex flex-md-row flex-column justify-content-between">
<div purpose="newletter-form-text" class="d-flex flex-row align-items-center">
<p v-if="formErrors.emailAddress || cloudError" class="text-danger" focus-first>Please enter a valid work email</p>
<p v-else-if="cloudSuccess">Thanks for subscribing!</p>
<p v-else><strong>Curious?</strong> Get product updates and news</p>
</div>
<ajax-form class="d-flex flex-md-row flex-column" action="createOrUpdateOneNewsletterSubscription" :form-errors.sync="formErrors" :form-data="formData" :form-rules="formRules" :syncing.sync="syncing" :cloud-error.sync="cloudError" @submitted="submittedNewsletterForm()">
<div class="d-flex flex-column w-100">
<input purpose="newsletter-form-input" class="form-control" v-model.trim="formData.emailAddress" placeholder="Enter your work email">
</div>
<div class="d-flex flex-column w-100">
<ajax-button purpose="subscribe-button" type="submit-button" :syncing="syncing" class="btn btn-primary">Subscribe</ajax-button>
</div>
</ajax-form>
</div>
</div>
</div>
<modal v-if="modal === 'unsubscribed'" @close="closeModal()">
<p class="mb-0">Your email preferences have been updated.</p>
</modal>