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; + } + } +}