Skip to content

Commit

Permalink
Merge pull request #238 from fcoronelmakingsense/promotions-banner
Browse files Browse the repository at this point in the history
feat: add dynamic promotions in public pages
  • Loading branch information
fcoronelmakingsense authored Jun 27, 2019
2 parents f859f02 + c43efb1 commit 76d082f
Show file tree
Hide file tree
Showing 16 changed files with 314 additions and 54 deletions.
1 change: 1 addition & 0 deletions .env.development
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
REACT_APP_DOPPLER_LEGACY_URL=http://localhost:52191
REACT_APP_DOPPLER_SITES_URL= https://www.fromdoppler.com
REACT_APP_DATAHUB_URL=https://hubapisecint.fromdoppler.com
REACT_APP_RECAPTCHA_PUBLIC_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
REACT_APP_USE_DOPPLER_LEGACY_LOGIN=false
Expand Down
1 change: 1 addition & 0 deletions .env.int
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
REACT_APP_DOPPLER_LEGACY_URL=https://appint.fromdoppler.net
REACT_APP_DOPPLER_SITES_URL= https://www.fromdoppler.com
REACT_APP_DATAHUB_URL=https://hubapisecint.fromdoppler.com
REACT_APP_RECAPTCHA_PUBLIC_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
REACT_APP_USE_DOPPLER_LEGACY_LOGIN=false
Expand Down
1 change: 1 addition & 0 deletions .env.production
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
REACT_APP_DOPPLER_LEGACY_URL=https://app2.fromdoppler.com
REACT_APP_DOPPLER_SITES_URL= https://www.fromdoppler.com
REACT_APP_DATAHUB_URL=https://hubapisec.fromdoppler.com
REACT_APP_RECAPTCHA_PUBLIC_KEY=6LddzZ8UAAAAAPSs09txKtTl9ewIyqYihfOC-dzf
REACT_APP_USE_DOPPLER_LEGACY_LOGIN=false
Expand Down
1 change: 1 addition & 0 deletions .env.qa
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
REACT_APP_DOPPLER_LEGACY_URL=https://appqa.fromdoppler.net
REACT_APP_DOPPLER_SITES_URL= https://www.fromdoppler.com
REACT_APP_DATAHUB_URL=https://hubapisecqa.fromdoppler.com
REACT_APP_RECAPTCHA_PUBLIC_KEY=6LeIxAcTAAAAAJcZVRqyHh71UMIEGNQ_MXjiZKhI
REACT_APP_USE_DOPPLER_LEGACY_LOGIN=false
Expand Down
19 changes: 2 additions & 17 deletions src/components/ForgotPassword/ForgotPassword.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import './ForgotPassword.css';
import { FormattedMessageMarkdown } from '../../i18n/FormattedMessageMarkdown';
import { Helmet } from 'react-helmet';
import { connect } from 'formik';
import Promotions from '../shared/Promotions/Promotions';

const fieldNames = {
email: 'email',
Expand Down Expand Up @@ -149,23 +150,7 @@ const ForgotPassword = ({ intl, location, dependencies: { dopplerLegacyClient }
</p>
</footer>
</article>
<section className="feature-panel">
<div
className="feature-panel--bg"
style={{
backgroundImage: `url('https://cdn.fromdoppler.com/doppler-ui-library/v2.5.0/img/violet-yellow.png')`,
}}
>
<article className="feature-content">
<h6>{_('feature_panel.forms')}</h6>
<h1>{_('feature_panel.forms_description')}</h1>
<p>{_('feature_panel.forms_remarks')}</p>
</article>
<figure className="content-img">
<img src={_('forgot_password.image_path')} alt="Subscription Forms" />
</figure>
</div>
</section>
<Promotions type="login" disabledSitesContent />
</main>
);
};
Expand Down
19 changes: 2 additions & 17 deletions src/components/Login/Login.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import { InjectAppServices } from '../../services/pure-di';
import { LoginErrorAccountNotValidated } from './LoginErrorAccountNotValidated';
import { FormattedMessageMarkdown } from '../../i18n/FormattedMessageMarkdown';
import { connect } from 'formik';
import Promotions from '../shared/Promotions/Promotions';

const fieldNames = {
user: 'user',
Expand Down Expand Up @@ -199,23 +200,7 @@ const Login = ({ intl, location, dependencies: { dopplerLegacyClient, sessionMan
</p>
</footer>
</article>
<section className="feature-panel">
<div
className="feature-panel--bg"
style={{
backgroundImage: `url('https://cdn.fromdoppler.com/doppler-ui-library/v2.5.0/img/violet-yellow.png')`,
}}
>
<article className="feature-content">
<h6>{_('feature_panel.forms')}</h6>
<h1>{_('feature_panel.forms_description')}</h1>
<p>{_('feature_panel.forms_remarks')}</p>
</article>
<figure className="content-img">
<img src={_('login.image_path')} alt="Subscription Forms" />
</figure>
</div>
</section>
<Promotions type="login" disabledSitesContent />
</main>
);
};
Expand Down
33 changes: 13 additions & 20 deletions src/components/Signup/Signup.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@ import {
import LanguageSelector from '../shared/LanguageSelector/LanguageSelector';
import SignupConfirmation from './SignupConfirmation';
import { FormattedMessageMarkdown } from '../../i18n/FormattedMessageMarkdown';
import Promotions from '../shared/Promotions/Promotions';
import queryString from 'query-string';

const fieldNames = {
firstname: 'firstname',
Expand All @@ -33,6 +35,12 @@ const minLength = {
errorMessageKey: 'validation_messages.error_min_length_2',
};

/** Extract the page parameter from url*/
function extractPage(location) {
const parsedQuery = location && location.search && queryString.parse(location.search);
return (parsedQuery && (parsedQuery['page'] || parsedQuery['Page'])) || null;
}

/** Prepare empty values for all fields
* It is required because in another way, the fields are not marked as touched.
*/
Expand All @@ -48,7 +56,7 @@ const getFormInitialValues = () =>
* @param { import('react-intl').InjectedIntl } props.intl
* @param { import('../../services/pure-di').AppServices } props.dependencies
*/
const Signup = function({ intl, dependencies: { dopplerLegacyClient, originResolver } }) {
const Signup = function({ intl, location, dependencies: { dopplerLegacyClient, originResolver } }) {
const _ = (id, values) => intl.formatMessage({ id: id }, values);

const [registeredUser, setRegisteredUser] = useState(null);
Expand Down Expand Up @@ -143,7 +151,9 @@ const Signup = function({ intl, dependencies: { dopplerLegacyClient, originResol
<h5>{_('signup.sign_up')}</h5>
<p className="content-subtitle">
{_('signup.sign_up_sub')} {_('signup.do_you_already_have_an_account')}{' '}
<Link to="/login">{_('signup.log_in')}</Link>
<Link to="/login" className="link--title">
{_('signup.log_in')}
</Link>
</p>
<FormWithCaptcha
className="signup-form"
Expand Down Expand Up @@ -224,24 +234,7 @@ const Signup = function({ intl, dependencies: { dopplerLegacyClient, originResol
</p>
</footer>
</article>
<section className="feature-panel">
<div
className="feature-panel--bg"
style={{
backgroundImage: `url('https://cdn.fromdoppler.com/doppler-ui-library/v2.5.0/img/violet-yellow.png')`,
color: `#FFFFFF`,
}}
>
<article className="feature-content">
<h6>{_('feature_panel.email_automation')}</h6>
<h1>{_('feature_panel.email_automation_description')}</h1>
<p>{_('feature_panel.email_automation_remarks')}</p>
</article>
<figure className="content-img">
<img src={_('signup.image_path')} alt="Automation" />
</figure>
</div>
</section>
<Promotions type="signup" page={extractPage(location)} disabledSitesContent />
</main>
);
};
Expand Down
79 changes: 79 additions & 0 deletions src/components/shared/Promotions/Promotions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
import React, { useState, useEffect } from 'react';
import { injectIntl } from 'react-intl';
import { InjectAppServices } from '../../../services/pure-di';
import Loading from '../../Loading/Loading';

const getDefaultBannerData = (intl) => {
const _ = (id, values) => intl.formatMessage({ id: id }, values);

return {
title: _('default_banner_data.title'),
description: _('default_banner_data.description'),
backgroundUrl: _('default_banner_data.background_url'),
imageUrl: _('default_banner_data.image_url'),
functionality: _('default_banner_data.functionality'),
fontColor: '#000',
};
};

/**
* Promotions
* @param { Object } props
* @param { import('react-intl').InjectedIntl } props.intl
* @param { import('../../services/pure-di').AppServices } props.dependencies
*/
const Promotions = function({
intl,
type,
page,
disabledSitesContent,
dependencies: { dopplerSitesClient },
}) {
const [bannerData, setBannerData] = useState({});
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
if (disabledSitesContent) {
setBannerData(getDefaultBannerData(intl));
setIsLoading(false);
} else {
const fetchData = async () => {
setIsLoading(true);
const bannerData = await dopplerSitesClient.getBannerData(intl.locale, type, page || '');
setBannerData(
bannerData.success && bannerData.value ? bannerData.value : getDefaultBannerData(intl),
);
setIsLoading(false);
};

fetchData();
}
}, [disabledSitesContent, dopplerSitesClient, page, intl, type]);

return (
<section className="feature-panel" style={{ position: 'relative' }}>
{isLoading ? (
<Loading />
) : (
<div
className="feature-panel--bg"
style={{
backgroundImage: `url(${bannerData.backgroundUrl})`,
color: bannerData.fontColor,
}}
>
<article className="feature-content">
<h6>{bannerData.functionality}</h6>
<h1>{bannerData.title}</h1>
<p>{bannerData.description}</p>
</article>
<figure className="content-img">
<img src={bannerData.imageUrl} alt="" />
</figure>
</div>
)}
</section>
);
};

export default InjectAppServices(injectIntl(Promotions));
84 changes: 84 additions & 0 deletions src/components/shared/Promotions/Promotions.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import React from 'react';
import { render, cleanup, waitForDomChange } from '@testing-library/react';
import 'jest-dom/extend-expect';
import Promotions from './Promotions';
import DopplerIntlProvider from '../../../i18n/DopplerIntlProvider.double-with-ids-as-values';
import { AppServicesProvider } from '../../../services/pure-di';
import { async } from 'q';

const emptyResponse = { success: false, error: new Error('Dummy error') };
const fullResponse = {
success: true,
value: {
title: 'default_banner_data.title',
description: 'default_banner_data.description',
backgroundUrl: 'default_banner_data.background_url',
imageUrl: 'default_banner_data.image_url',
functionality: 'default_banner_data.functionality',
color: '#fff',
},
};

describe('Promotions Component', () => {
afterEach(cleanup);
it('should sites response fail', async () => {
const dopplerSitesClientDouble = {
getBannerData: async () => emptyResponse,
};

const { container, getByText } = render(
<AppServicesProvider
forcedServices={{
dopplerSitesClient: dopplerSitesClientDouble,
}}
>
<DopplerIntlProvider locale="es">
<Promotions type="signup" page="example" />
</DopplerIntlProvider>
</AppServicesProvider>,
);
expect(container.querySelector('.loading-box')).toBeInTheDocument();
await waitForDomChange();
expect(getByText('default_banner_data.title'));
});

it('should disabled sites content', () => {
const dopplerSitesClientDouble = {
getBannerData: async () => emptyResponse,
};

const { getByText } = render(
<AppServicesProvider
forcedServices={{
dopplerSitesClient: dopplerSitesClientDouble,
}}
>
<DopplerIntlProvider locale="es">
<Promotions type="signup" page="example" disabledSitesContent />
</DopplerIntlProvider>
</AppServicesProvider>,
);
expect(getByText('default_banner_data.title'));
});

it('should has full data from service', async () => {
const dopplerSitesClientDouble = {
getBannerData: async () => fullResponse,
};

const { container, getByText } = render(
<AppServicesProvider
forcedServices={{
dopplerSitesClient: dopplerSitesClientDouble,
}}
>
<DopplerIntlProvider locale="es">
<Promotions type="signup" page="example" />
</DopplerIntlProvider>
</AppServicesProvider>,
);
expect(container.querySelector('.loading-box')).toBeInTheDocument();
await waitForDomChange();
expect(getByText(fullResponse.value.title));
});
});
3 changes: 3 additions & 0 deletions src/doppler-types.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
export type UnexpectedError = { success?: false; message?: string | null; error?: any };
export type ErrorResult<TError> = { success?: false; expectedError: TError } | UnexpectedError;
export type Result<TResult, TError> = { success: true; value: TResult } | ErrorResult<TError>;
export type ResultWithoutExpectedErrors<TResult> =
| { success: true; value: TResult }
| UnexpectedError;
export type EmptyResult<TError> = { success: true } | ErrorResult<TError>;
// It does not work:
// type EmptyResult = { success: true } | UnexpectedError;
Expand Down
7 changes: 7 additions & 0 deletions src/i18n/en.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export default {
send: `Send`,
show: `Show`,
},
default_banner_data: {
background_url: 'https://cdn.fromdoppler.com/doppler-ui-library/v2.5.0/img/violet-yellow.png',
description: 'Classics and pop-ups with Single or Double Opt-In subscription. You decide how you want them to look, what data to request and where to place them!',
functionality: 'subscription forms',
image_url: 'https://cdn.fromdoppler.com/doppler-ui-library/v2.5.0/img/login-en.png',
title: 'Add new contacts to your Lists using custom Forms',
},
empty_notification_text: `You don't have pending notifications.`,
feature_panel: {
email_automation: `Email Automation`,
Expand Down
7 changes: 7 additions & 0 deletions src/i18n/es.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,13 @@ export default {
send: `Enviar`,
show: `Mostrar`,
},
default_banner_data: {
background_url: 'https://cdn.fromdoppler.com/doppler-ui-library/v2.5.0/img/violet-yellow.png',
description: 'Clásicos y pop-ups con suscripción Simple o Doble Opt-In. ¡Tú eliges cómo quieres que luzcan, qué datos solicitar y dónde ubicarlos!',
functionality: 'Formularios de suscripción',
image_url: 'https://cdn.fromdoppler.com/doppler-ui-library/v2.5.0/img/login-es.png',
title: 'Suma contactos a tus Listas con Formularios personalizados',
},
empty_notification_text: `No tienes notificaciones pendientes.`,
feature_panel: {
email_automation: `Email Automation`,
Expand Down
2 changes: 2 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import ReactGA from 'react-ga';
// Only used in development environment, it does not affect production build
import { HardcodedDopplerLegacyClient } from './services/doppler-legacy-client.doubles';
import { HardcodedDatahubClient } from './services/datahub-client.doubles';
import { HardcodedDopplerSitesClient } from './services/doppler-sites-client.doubles';
import { polyfill } from 'es6-object-assign';
import 'polyfill-array-includes';
import 'promise-polyfill/src/polyfill';
Expand All @@ -29,6 +30,7 @@ const forcedServices =
process.env.NODE_ENV === 'development'
? {
dopplerLegacyClient: new HardcodedDopplerLegacyClient(),
dopplerSitesClient: new HardcodedDopplerSitesClient(),
datahubClient: new HardcodedDatahubClient(),
shopifyClient: new HardcodedShopifyClient(),
}
Expand Down
Loading

0 comments on commit 76d082f

Please sign in to comment.