Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

1609 Detect existing users and send modify subscriptions email #1612

Merged
merged 8 commits into from
Dec 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 6 additions & 18 deletions client/app/components/subscription-form.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { action, computed, set } from '@ember/object';
import fetch from 'fetch';
import ENV from 'labs-zap-search/config/environment';
import { getCommunityDistrictsByBorough } from '../helpers/lookup-community-district';
import { validateEmail } from '../helpers/validate-email';

export default class SubscriptionFormComponent extends Component {
communityDistrictsByBorough = {};
Expand Down Expand Up @@ -43,14 +44,17 @@ export default class SubscriptionFormComponent extends Component {
}

// eslint-disable-next-line ember/use-brace-expansion
@computed('isCommunityDistrict', 'args.subscriptions', 'args.email')
@computed('isCommunityDistrict', 'args.subscriptions', 'args.email', 'args.invalidEmailForSignup')
get canBeSubmitted() {
// If it's an update, subscriptions must be different, or they must have unchecked CD Updates
if (this.args.isUpdate) {
if (this.previousIsCommunityDistrict && !this.isCommunityDistrict) return true;
if (!(Object.entries(this.args.subscriptions).find(([key, value]) => (this.previousSubscriptions[key] !== value)))) return false;
}

// Disable signup with existing email addresses
if (this.args.invalidEmailForSignup) return false;

if ((this.isCommunityDistrict && !this.isAtLeastOneCommunityDistrictSelected)) return false;
return this.isEmailValid
&& (this.args.subscriptions.CW
Expand All @@ -59,23 +63,7 @@ export default class SubscriptionFormComponent extends Component {

@computed('args.email')
get isEmailValid() {
// eslint-disable-next-line no-useless-escape
const tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
if (!this.args.email) return false;

if (this.args.email.length > 254) return false;

const valid = tester.test(this.args.email);
if (!valid) return false;

// Further checking of some things regex can't handle
const parts = this.args.email.split('@');
if (parts[0].length > 64) return false;

const domainParts = parts[1].split('.');
if (domainParts.some(function(part) { return part.length > 63; })) return false;

return true;
return validateEmail(this.args.email);
}

@computed('args.subscriptions')
Expand Down
95 changes: 95 additions & 0 deletions client/app/controllers/subscribe.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
import Controller from '@ember/controller';
import { action, computed } from '@ember/object';
import fetch from 'fetch';
import ENV from 'labs-zap-search/config/environment';
import { validateEmail } from '../helpers/validate-email';

export default class SubscribeController extends Controller {
lastEmailChecked = '';

emailAlreadyExists = false;

emailNeedsConfirmation = false;

startContinuouslyChecking = false;

emailSent = false;

@computed('emailAlreadyExists', 'emailNeedsConfirmation')
get invalidEmailForSignup() {
return (this.emailAlreadyExists || this.emailNeedsConfirmation);
}

@action
async checkExistingEmail(event) {
const email = event.target.value;
if (email === this.lastEmailChecked) { return; }
this.set('lastEmailChecked', email);
this.set('startContinuouslyChecking', true);

try {
const response = await fetch(`${ENV.host}/subscribers/email/${email}`);
const userData = await response.json();

if (userData.error) {
this.set('emailAlreadyExists', false);
this.set('emailNeedsConfirmation', false);
this.set('emailSent', false);
return;
}

if (userData.confirmed === true) {
this.set('emailAlreadyExists', true);
this.set('emailNeedsConfirmation', false);
} else if (userData.confirmed === false) {
this.set('emailNeedsConfirmation', true);
this.set('emailAlreadyExists', false);
}
return;
} catch (error) {
// We will receive an error if:
// a) the user does not exist in Sendgrid, or
// b) their confirmed field is null.
// Either way, we don't need to log to console
this.set('emailAlreadyExists', false);
this.set('emailNeedsConfirmation', false);
this.set('emailSent', false);
}
}

@action
continuouslyCheckEmail(event) {
if ((this.startContinuouslyChecking) || (validateEmail(event.target.value))) { this.checkExistingEmail(event); }
}

@action
async sendEmail() {
if (this.emailAlreadyExists) {
// Run the script to update the email
try {
await fetch(`${ENV.host}/subscribers/${this.model.email}/modify`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
this.set('emailSent', true);
} catch (error) {
console.error(error); // eslint-disable-line
}
} else if (this.emailNeedsConfirmation) {
// Run the script to confirm the email
try {
await fetch(`${ENV.host}/subscribers/${this.model.email}/resend-confirmation`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
});
this.set('emailSent', true);
} catch (error) {
console.error(error); // eslint-disable-line
}
}
}
}
24 changes: 24 additions & 0 deletions client/app/helpers/validate-email.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
import { helper } from '@ember/component/helper';


export function validateEmail(email) {
// eslint-disable-next-line no-useless-escape
const tester = /^[-!#$%&'*+\/0-9=?A-Z^_a-z{|}~](\.?[-!#$%&'*+\/0-9=?A-Z^_a-z`{|}~])*@[a-zA-Z0-9](-*\.?[a-zA-Z0-9])*\.[a-zA-Z](-?[a-zA-Z0-9])+$/;
if (!email) return false;

if (email.length > 254) return false;

const valid = tester.test(email);
if (!valid) return false;

// Further checking of some things regex can't handle
const parts = email.split('@');
if (parts[0].length > 64) return false;

const domainParts = parts[1].split('.');
if (domainParts.some(function(part) { return part.length > 63; })) return false;

return true;
}

export default helper(validateEmail);
1 change: 1 addition & 0 deletions client/app/styles/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,7 @@ $completed-color: #a6cee3;
@import 'modules/_m-search';
@import 'modules/_m-site-header';
@import 'modules/_m-statuses';
@import 'modules/_m-subscribe';
@import 'modules/_m-subscribed';
@import 'modules/_m-subscribers-confirm';
@import 'modules/_m-subscription-form';
Expand Down
13 changes: 13 additions & 0 deletions client/app/styles/modules/_m-subscribe.scss
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
// --------------------------------------------------
// Module: Subscribe Page
// --------------------------------------------------

.email-exists, .email-needs-confirmation {
padding-top: 1.5rem;
font-weight: 700;
}

.email-sent {
color: $success-color;
font-weight: 700;
}
13 changes: 11 additions & 2 deletions client/app/templates/subscribe.hbs
Original file line number Diff line number Diff line change
Expand Up @@ -7,12 +7,21 @@
<span class="text-small">Get updated on milestones for all projects in any community district (CD) by signing up to receive
emails on Zoning Application Portal.</span>
</div>
<SubscriptionForm @subscriptions={{this.model.subscriptions}} @email={{this.model.email}} @isUpdate={{false}}>
<SubscriptionForm @subscriptions={{this.model.subscriptions}} @email={{this.model.email}} @isUpdate={{false}} @invalidEmailForSignup={{this.invalidEmailForSignup}}>
<div class="subscribe-section">
<div class="subscribe-input-group">
<label class="email-label">Email Address</label>
<Input class="input-group-field" type="text" @value={{this.model.email}}/>
<Input class="input-group-field" type="text" @value={{this.model.email}} onkeyup={{action 'continuouslyCheckEmail'}} onchange={{action 'checkExistingEmail'}} />
</div>
{{#if this.emailAlreadyExists}}
<p class="email-exists">This email already is subscribed to ZAP Updates. <a onclick={{action 'sendEmail'}}>Click here to receive an email to modify subscriptions.</a></p>
{{/if}}
{{#if this.emailNeedsConfirmation}}
<p class="email-needs-confirmation">This email address has not confirmed their subscription to ZAP Updates. <a onclick={{action 'sendEmail'}}>Click here to re-send the confirmation email.</a></p>
{{/if}}
{{#if this.emailSent}}
<p class="email-sent">Email sent.</p>
{{/if}}
</div>
</SubscriptionForm>
<div class="subscribe-footer">
Expand Down
4 changes: 2 additions & 2 deletions client/ember-cli-build.js
Original file line number Diff line number Diff line change
Expand Up @@ -49,9 +49,9 @@ module.exports = function(defaults) {
// Use `app.import` to add additional libraries to the generated
// output files.
if (app.env === 'test') {
app.import('node_modules/foundation-sites/dist/js/foundation.js', {type: "test"});
app.import('node_modules/foundation-sites/dist/js/foundation.js', { type: 'test' });
} else {
app.import('node_modules/foundation-sites/dist/js/foundation.min.js', {type: "vendor"});
app.import('node_modules/foundation-sites/dist/js/foundation.min.js', { type: 'vendor' });
}
// If you need to use different assets in different
// environments, specify an object as the first parameter. That
Expand Down
Loading