diff --git a/.circleci/config.yml b/.circleci/config.yml
index db968e8c4c..98051a8441 100644
--- a/.circleci/config.yml
+++ b/.circleci/config.yml
@@ -236,8 +236,8 @@ workflows:
context : org-global
filters:
branches:
- only:
- - gig-details-fixes
+ only:
+ - hot-fix
# This is alternate dev env for parallel testing
- "build-qa":
context : org-global
diff --git a/src/shared/components/Contentful/AppComponent/index.jsx b/src/shared/components/Contentful/AppComponent/index.jsx
index bc9971fa63..47251bfcfc 100644
--- a/src/shared/components/Contentful/AppComponent/index.jsx
+++ b/src/shared/components/Contentful/AppComponent/index.jsx
@@ -10,6 +10,8 @@ import React from 'react';
import { errors } from 'topcoder-react-lib';
import Leaderboard from 'containers/tco/Leaderboard';
import RecruitCRMJobs from 'containers/Gigs/RecruitCRMJobs';
+import EmailSubscribeForm from 'containers/EmailSubscribeForm';
+
const { fireErrorMessage } = errors;
@@ -34,6 +36,9 @@ export function AppComponentSwitch(appComponent) {
if (appComponent.fields.type === 'RecruitCRM-Jobs') {
return ;
}
+ if (appComponent.fields.type === 'EmailSubscribeForm') {
+ return ;
+ }
fireErrorMessage('Unsupported app component type from contentful', '');
return null;
}
diff --git a/src/shared/containers/EmailSubscribeForm/index.jsx b/src/shared/containers/EmailSubscribeForm/index.jsx
new file mode 100644
index 0000000000..5b9fe1d654
--- /dev/null
+++ b/src/shared/containers/EmailSubscribeForm/index.jsx
@@ -0,0 +1,221 @@
+/**
+ * Genetic subscribe for MailChimp tags component
+ */
+import React from 'react';
+import PT from 'prop-types';
+import { isValidEmail } from 'utils/tc';
+import TextInput from 'components/GUIKit/TextInput';
+import _ from 'lodash';
+import LoadingIndicator from 'components/LoadingIndicator';
+import { Link } from 'topcoder-react-utils';
+import defaulTheme from './style.scss';
+
+/* Holds the base URL of Community App endpoints that proxy HTTP request to
+ * mailchimp APIs. */
+const PROXY_ENDPOINT = '/api/mailchimp';
+
+class SubscribeMailChimpTagContainer extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ formErrors: {},
+ formData: {},
+ };
+ this.onSubscribeClick = this.onSubscribeClick.bind(this);
+ this.onFormInputChange = this.onFormInputChange.bind(this);
+ this.validateForm = this.validateForm.bind(this);
+ }
+
+ onSubscribeClick() {
+ this.validateForm();
+ // eslint-disable-next-line consistent-return
+ this.setState((state) => {
+ const { formData, formErrors } = state;
+ if (_.isEmpty(formErrors)) {
+ const { listId, tags } = this.props;
+ const fetchUrl = `${PROXY_ENDPOINT}/${listId}/members/${formData.email}/tags`;
+ const data = {
+ email_address: formData.email,
+ status: 'subscribed',
+ tags: tags.map(t => ({ name: t, status: 'active' })),
+ merge_fields: {
+ FNAME: formData.fname,
+ LNAME: formData.lname,
+ },
+ };
+ fetch(fetchUrl, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ }).then(result => result.json()).then((dataResponse) => {
+ if (dataResponse.status === 204) {
+ // regist success
+ return this.setState({
+ subscribing: false,
+ subsribed: true,
+ error: '',
+ });
+ }
+ if (dataResponse.status === 404) {
+ // new email register it for list and add tags
+ data.tags = tags;
+ return fetch(`${PROXY_ENDPOINT}/${listId}/members`, {
+ method: 'POST',
+ headers: {
+ 'Content-Type': 'application/json',
+ },
+ body: JSON.stringify(data),
+ })
+ .then(result => result.json()).then((rsp) => {
+ this.setState({
+ subscribing: false,
+ subsribed: !rsp.detail,
+ error: rsp.detail ? rsp.title : '',
+ });
+ });
+ }
+ return this.setState({
+ subscribing: false,
+ subsribed: false,
+ error: `Error ${dataResponse.status} when assigning tags to ${formData.email}`,
+ });
+ })
+ .catch((e) => {
+ this.setState({
+ subscribing: false,
+ subsribed: false,
+ error: e.message,
+ });
+ });
+ return { subscribing: true };
+ }
+ });
+ }
+
+ onFormInputChange(key, val) {
+ this.setState((state) => {
+ const { formData } = state;
+ formData[key] = val;
+ return {
+ ...state,
+ formData,
+ };
+ });
+ this.validateForm(key);
+ }
+
+ validateForm(key) {
+ this.setState((state) => {
+ const { formData, formErrors } = state;
+ if (key) {
+ // validate only the key
+ if (!formData[key] || !_.trim(formData[key])) formErrors[key] = 'Required field';
+ else if (key === 'email' && !(isValidEmail(formData.email))) formErrors.email = 'Invalid email';
+ else delete formErrors[key];
+ } else {
+ _.each(['fname', 'lname', 'email'], (rkey) => {
+ if (!formData[rkey] || !_.trim(formData[rkey])) formErrors[rkey] = 'Required field';
+ else if (rkey === 'email' && !(isValidEmail(formData.email))) formErrors.email = 'Invalid email';
+ else delete formErrors[key];
+ });
+ }
+ // updated state
+ return {
+ ...state,
+ formErrors,
+ };
+ });
+ }
+
+ render() {
+ const {
+ formData, formErrors, subscribing, subsribed, error,
+ } = this.state;
+ const {
+ btnText, title, successTitle, successText, successLink, successLinkText,
+ } = this.props;
+ return (
+
+ {
+ subscribing ? (
+
+
+
+ Processing your subscription...
+
+
+ ) : null
+ }
+ {
+ subsribed || error ? (
+
+
{error ? 'OOPS!' : successTitle}
+
{error || successText}
+ {
+ error
+ ?
+ :
{successLinkText}
+ }
+
+ ) : null
+ }
+ {
+ !subscribing && !subsribed && !error ? (
+
+ {title}
+ this.onFormInputChange('fname', val)}
+ errorMsg={formErrors.fname}
+ value={formData.fname}
+ required
+ />
+ this.onFormInputChange('lname', val)}
+ errorMsg={formErrors.lname}
+ value={formData.lname}
+ required
+ />
+ this.onFormInputChange('email', val)}
+ errorMsg={formErrors.email}
+ value={formData.email}
+ required
+ />
+
+
+ ) : null
+ }
+
+ );
+ }
+}
+
+SubscribeMailChimpTagContainer.defaultProps = {
+ title: '',
+ btnText: '',
+ successTitle: 'Success!',
+ successText: '',
+ successLink: '',
+ successLinkText: '',
+};
+
+SubscribeMailChimpTagContainer.propTypes = {
+ listId: PT.string.isRequired,
+ tags: PT.arrayOf(PT.string).isRequired,
+ title: PT.string,
+ btnText: PT.string,
+ successTitle: PT.string,
+ successText: PT.string,
+ successLink: PT.string,
+ successLinkText: PT.string,
+};
+
+export default SubscribeMailChimpTagContainer;
diff --git a/src/shared/containers/EmailSubscribeForm/style.scss b/src/shared/containers/EmailSubscribeForm/style.scss
new file mode 100644
index 0000000000..4355e4cfd6
--- /dev/null
+++ b/src/shared/containers/EmailSubscribeForm/style.scss
@@ -0,0 +1,98 @@
+@import "~components/Contentful/default";
+
+.loadingWrap {
+ margin: 0;
+ padding: 0 34px;
+
+ @include gui-kit-headers;
+ @include gui-kit-content;
+ @include roboto-regular;
+
+ .loadingText {
+ font-family: Roboto, sans-serif;
+ color: #2a2a2a;
+ text-align: center;
+ margin-top: 26px;
+ }
+}
+
+.subscribedWrap {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ padding: 0 34px;
+
+ @include gui-kit-headers;
+ @include gui-kit-content;
+ @include roboto-regular;
+
+ > h4 {
+ margin-bottom: 16px !important;
+ }
+
+ > p {
+ font-size: 24px !important;
+ line-height: 36px !important;
+ text-align: center;
+
+ &.errorMsg {
+ color: #ef476f;
+ }
+ }
+}
+
+.wrapper {
+ display: flex;
+ flex-direction: column;
+ color: #2a2a2a;
+
+ @include gui-kit-headers;
+ @include gui-kit-content;
+ @include roboto-regular;
+
+ h6 {
+ margin-top: 41px;
+
+ @include xs-to-sm {
+ margin-top: 0;
+ }
+ }
+
+ > div {
+ margin-bottom: 8px;
+ }
+
+ .button {
+ background-color: #137d60;
+ border-radius: 20px;
+ color: #fff !important;
+ font-size: 14px;
+ font-weight: bolder;
+ text-decoration: none;
+ text-transform: uppercase;
+ line-height: 40px;
+ padding: 0 20px;
+ border: none;
+ outline: none;
+ margin-top: 13px;
+ margin-bottom: 41px;
+ max-width: 150px;
+
+ &:hover {
+ box-shadow: 0 1px 5px 0 rgba(0, 0, 0, 0.2);
+ background-color: #0ab88a;
+ }
+
+ @include xs-to-sm {
+ margin-bottom: 20px;
+ }
+
+ &:disabled {
+ background-color: #e9e9e9 !important;
+ border: none !important;
+ text-decoration: none !important;
+ color: #fafafb !important;
+ box-shadow: none !important;
+ }
+ }
+}