Merge pull request #1263 from mailpoet/am_display_events_chunk2

Adds necessary changes in order to display automatic events in Free and Premium [PREMIUM-65] [PREMIUM-63]
This commit is contained in:
mrcasual
2018-02-14 12:41:06 -05:00
committed by GitHub
13 changed files with 493 additions and 59 deletions

View File

@@ -178,6 +178,7 @@ define([
target: {
value: transformedValue,
name: this.props.field.name,
id: e.target.id,
},
});
}

View File

@@ -2,9 +2,30 @@ import React from 'react';
const FormFieldText = React.createClass({
render() {
let value = this.props.item[this.props.field.name];
if (value === undefined) {
value = this.props.field.defaultValue || '';
const name = this.props.field.name || null;
const item = this.props.item || {};
let value;
let defaultValue;
// value should only be set when onChangeValue is configured
if (this.props.onValueChange instanceof Function) {
value = item[this.props.field.name];
// set value to defaultValue if available
value = (value === undefined && this.props.field.defaultValue) ?
this.props.field.defaultValue : value;
}
// defaultValue should only be set only when value is not set
if (!value && this.props.field.defaultValue) {
defaultValue = this.props.field.defaultValue;
}
let id = this.props.field.id || null;
if (!id && this.props.field.name) {
id = `field_${this.props.field.name}`;
}
let className = this.props.field.class || null;
if (!className && !this.props.field.size) {
className = 'regular-text';
}
return (
@@ -15,15 +36,16 @@ const FormFieldText = React.createClass({
? this.props.field.disabled(this.props.item)
: false
}
className={(this.props.field.size) ? '' : 'regular-text'}
className={className}
size={
(this.props.field.size !== 'auto' && this.props.field.size > 0)
? this.props.field.size
: false
}
name={this.props.field.name}
id={`field_${this.props.field.name}`}
name={name}
id={id}
value={value}
defaultValue={defaultValue}
placeholder={this.props.field.placeholder}
onChange={this.props.onValueChange}
{...this.props.field.validation}

View File

@@ -15,27 +15,28 @@ define(
const Breadcrumb = React.createClass({
getInitialState: function () {
const steps = this.props.steps || [
{
name: 'type',
label: MailPoet.I18n.t('selectType'),
link: '/new',
},
{
name: 'template',
label: MailPoet.I18n.t('template'),
},
{
name: 'editor',
label: MailPoet.I18n.t('designer'),
},
{
name: 'send',
label: MailPoet.I18n.t('send'),
},
];
return {
step: null,
steps: [
{
name: 'type',
label: MailPoet.I18n.t('selectType'),
link: '/new',
},
{
name: 'template',
label: MailPoet.I18n.t('template'),
},
{
name: 'editor',
label: MailPoet.I18n.t('designer'),
},
{
name: 'send',
label: MailPoet.I18n.t('send'),
},
],
steps: steps,
};
},
render: function () {

View File

@@ -3,14 +3,14 @@ import ReactDOM from 'react-dom';
import { Router, Route, IndexRedirect, useRouterHistory } from 'react-router';
import { createHashHistory } from 'history';
import Hooks from 'wp-js-hooks';
import _ from 'underscore';
import NewsletterTypes from 'newsletters/types.jsx';
import NewsletterTemplates from 'newsletters/templates.jsx';
import NewsletterSend from 'newsletters/send.jsx';
import NewsletterTypeStandard from 'newsletters/types/standard.jsx';
import NewsletterTypeNotification from 'newsletters/types/notification/notification.jsx';
import AutomaticEmailsEventsList from 'newsletters/types/automatic_emails/events_list.jsx';
import NewsletterListStandard from 'newsletters/listings/standard.jsx';
import NewsletterListWelcome from 'newsletters/listings/welcome.jsx';
import NewsletterListNotification from 'newsletters/listings/notification.jsx';
@@ -26,33 +26,82 @@ const App = React.createClass({
const container = document.getElementById('newsletters_container');
const getAutomaticEmailsRoutes = () => {
if (!window.mailpoet_automatic_emails) return null;
return _.map(window.mailpoet_automatic_emails, automaticEmail => ({
path: `new/${automaticEmail.id}`,
name: automaticEmail.id,
component: AutomaticEmailsEventsList,
data: {
automaticEmail: automaticEmail,
},
}));
};
if (container) {
let extraRoutes = [];
extraRoutes = Hooks.applyFilters('mailpoet_newsletters_before_router', extraRoutes);
let routes = [
/* Listings */
{
path: 'standard(/)**',
params: { tab: 'standard' },
component: NewsletterListStandard,
},
{
path: 'welcome(/)**',
component: NewsletterListWelcome,
},
{
path: 'notification/history/:parent_id(/)**',
component: NewsletterListNotificationHistory,
},
{
path: 'notification(/)**',
component: NewsletterListNotification,
},
/* Newsletter: type selection */
{
path: 'new',
component: NewsletterTypes,
},
/* New newsletter: types */
{
path: 'new/standard',
component: NewsletterTypeStandard,
},
{
path: 'new/notification',
component: NewsletterTypeNotification,
},
/* Template selection */
{
name: 'template',
path: 'template/:id',
component: NewsletterTemplates,
},
/* Sending options */
{
path: 'send/:id',
component: NewsletterSend,
},
];
routes = Hooks.applyFilters('mailpoet_newsletters_before_router', [...routes, ...getAutomaticEmailsRoutes()]);
const mailpoetListing = ReactDOM.render((
<Router history={history}>
<Route path="/" component={App}>
<IndexRedirect to="standard" />
{/* Listings */}
<Route path="standard(/)**" params={{ tab: 'standard' }} component={NewsletterListStandard} />
<Route path="welcome(/)**" component={NewsletterListWelcome} />
<Route path="notification/history/:parent_id(/)**"
component={NewsletterListNotificationHistory} />
<Route path="notification(/)**" component={NewsletterListNotification} />
{/* Newsletter: type selection */}
<Route path="new" component={NewsletterTypes} />
{/* New newsletter: types */}
<Route path="new/standard" component={NewsletterTypeStandard} />
<Route path="new/notification" component={NewsletterTypeNotification} />
{/* Template selection */}
<Route name="template" path="template/:id" component={NewsletterTemplates} />
{/* Sending options */}
<Route path="send/:id" component={NewsletterSend} />
{/* Extra routes */}
{extraRoutes.map(rt =>
<Route key={rt.path} path={rt.path} component={rt.component} data={rt.data || null} />
)}
{routes.map(route => (
<Route
key={route.path}
path={route.path}
component={route.component}
name={route.name || null}
params={route.params || null}
data={route.data || null}
/>
))}
</Route>
</Router>
), container);

View File

@@ -5,13 +5,15 @@ define(
'wp-js-hooks',
'react-router',
'newsletters/breadcrumb.jsx',
'underscore',
],
(
React,
MailPoet,
Hooks,
Router,
Breadcrumb
Breadcrumb,
_
) => {
const NewsletterTypes = React.createClass({
contextTypes: {
@@ -50,8 +52,29 @@ define(
}
});
},
getAutomaticEmails: function () {
if (!window.mailpoet_automatic_emails) return [];
return _.map(window.mailpoet_automatic_emails, (automaticEmail) => {
const email = automaticEmail;
const disabled = !email.events;
email.action = (() => (
<div>
<a className="button button-primary"
disabled={disabled}
onClick={!disabled ? this.setupNewsletter.bind(null, automaticEmail.id) : null}
>
{ MailPoet.I18n.t('setUp') }
</a>
</div>
))();
return email;
});
},
render: function () {
let types = [
const defaultTypes = [
{
id: 'standard',
title: MailPoet.I18n.t('regularNewsletterTypeTitle'),
@@ -72,7 +95,7 @@ define(
return (
<div>
<a href="?page=mailpoet-premium" target="_blank">
{MailPoet.I18n.t('getPremiumVersion')}
{MailPoet.I18n.t('premiumFeatureLink')}
</a>
</div>
);
@@ -92,7 +115,7 @@ define(
},
];
types = Hooks.applyFilters('mailpoet_newsletters_types', types, this);
const types = Hooks.applyFilters('mailpoet_newsletters_types', [...defaultTypes, ...this.getAutomaticEmails()], this);
return (
<div>

View File

@@ -0,0 +1,41 @@
import Breadcrumb from 'newsletters/breadcrumb.jsx';
import React from 'react';
import MailPoet from 'mailpoet';
class AutomaticEmailsBreadcrumb extends React.Component {
render() {
const steps = [
{
name: 'type',
label: MailPoet.I18n.t('selectType'),
link: '/new',
},
{
name: 'events',
label: MailPoet.I18n.t('events'),
},
{
name: 'conditions',
label: MailPoet.I18n.t('conditions'),
},
{
name: 'template',
label: MailPoet.I18n.t('template'),
},
{
name: 'editor',
label: MailPoet.I18n.t('designer'),
},
{
name: 'send',
label: MailPoet.I18n.t('send'),
},
];
return (
<Breadcrumb step={this.props.step} steps={steps} />
);
}
}
module.exports = AutomaticEmailsBreadcrumb;

View File

@@ -0,0 +1,93 @@
import React from 'react';
import AutomaticEmailsBreadcrumb from 'newsletters/types/automatic_emails/breadcrumb.jsx';
import MailPoet from 'mailpoet';
import _ from 'underscore';
class AutomaticEmailsEventsList extends React.Component {
constructor(props) {
super(props);
this.automaticEmail = this.props.route.data.automaticEmail;
this.automaticEmailEvents = this.automaticEmail.events;
this.eventsConfigurator = this.eventsConfigurator.bind(this);
}
eventsConfigurator(eventId) {
this.props.router.push(`new/${this.automaticEmail.id}/${eventId}/conditions`);
}
displayEvents() {
const events = _.map(this.automaticEmailEvents, (event, index) => {
let action;
if (this.automaticEmail.premium) {
action = (
<a href="?page=mailpoet-premium"
target="_blank">
{MailPoet.I18n.t('premiumFeatureLink')}
</a>
);
} else {
const disabled = event.soon;
action = (
<a className="button button-primary"
disabled={disabled}
onClick={!disabled ? this.eventsConfigurator.bind(null, event.id) : null}
>
{event.actionButtonTitle || MailPoet.I18n.t('setUp')}
</a>
);
}
return (
<li key={index} data-type={event.id}>
<div>
<div className="mailpoet_thumbnail">
{event.thumbnailImage ? <img src={event.thumbnailImage} /> : null}
</div>
<div className="mailpoet_description">
<div className="title_and_badge">
<h3>{event.title} {event.soon ? `(${MailPoet.I18n.t('soon').toLowerCase()})` : ''}</h3>
{event.badge ? (
<span className={`mailpoet_badge mailpoet_badge_${event.badge.style}`}>
{event.badge.text}
</span>
) : ''
}
</div>
<p>{event.description}</p>
</div>
<div className="mailpoet_actions">
{action}
</div>
</div>
</li>
);
});
return (
<ul className="mailpoet_boxes woocommerce clearfix">
{events}
</ul>
);
}
render() {
const heading = MailPoet.I18n.t('selectAutomaticEmailsEventsHeading')
.replace('%1s', this.automaticEmail.title);
return (
<div>
<h1>
{heading} ({MailPoet.I18n.t('beta').toLowerCase()})
</h1>
<AutomaticEmailsBreadcrumb step="events" />
{this.displayEvents()}
</div>
);
}
}
module.exports = AutomaticEmailsEventsList;

View File

@@ -0,0 +1,161 @@
define(
[
'react',
'mailpoet',
'wp-js-hooks',
'react-router',
'newsletters/breadcrumb.jsx',
],
(
React,
MailPoet,
Hooks,
Router,
Breadcrumb
) => {
const WooCommerceAutomaticEmail = React.createClass({
contextTypes: {
router: React.PropTypes.object.isRequired,
},
setupNewsletter: function (type) {
if (type !== undefined) {
this.context.router.push(`/new/${type}`);
MailPoet.trackEvent('Emails > Type selected', {
'MailPoet Free version': window.mailpoet_version,
'Email type': type,
});
}
},
createNewsletter: function (type) {
MailPoet.trackEvent('Emails > Type selected', {
'MailPoet Free version': window.mailpoet_version,
'Email type': type,
});
MailPoet.Ajax.post({
api_version: window.mailpoet_api_version,
endpoint: 'newsletters',
action: 'create',
data: {
type: type,
subject: MailPoet.I18n.t('draftNewsletterTitle'),
},
}).done((response) => {
this.context.router.push(`/template/${response.data.id}`);
}).fail((response) => {
if (response.errors.length > 0) {
MailPoet.Notice.error(
response.errors.map(error => error.message),
{ scroll: true }
);
}
});
},
render: function () {
const types = [
{
id: 'woocommerce',
title: MailPoet.I18n.t('wooCommerceEventAbandonedCartTitle'),
description: MailPoet.I18n.t('wooCommerceEventAbandonedCartDescription'),
badge: {
text: MailPoet.I18n.t('wooCommerceEventAbandonedCartBadge'),
style: 'red',
},
},
{
id: 'woocommerce',
title: MailPoet.I18n.t('wooCommerceEventFirstPurchaseTitle'),
description: MailPoet.I18n.t('wooCommerceEventFirstPurchaseDescription'),
badge: {
text: MailPoet.I18n.t('wooCommerceEventFirstPurchaseBadge'),
style: 'yellow',
},
},
{
id: 'woocommerce',
title: MailPoet.I18n.t('wooCommerceEventPurchasedProductTitle'),
description: MailPoet.I18n.t('wooCommerceEventPurchasedProductDescription'),
},
{
id: 'woocommerce',
title: MailPoet.I18n.t('wooCommerceEventPurchasedInCategoryTitle'),
description: MailPoet.I18n.t('wooCommerceEventPurchasedInCategoryDescription'),
soon: true,
},
{
id: 'woocommerce',
title: MailPoet.I18n.t('wooCommerceEventBigSpenderTitle'),
description: MailPoet.I18n.t('wooCommerceEventBigSpenderDescription'),
soon: true,
badge: {
text: MailPoet.I18n.t('wooCommerceEventSmartToHaveBadge'),
style: 'teal',
},
},
];
const steps = [
{
name: 'type',
label: MailPoet.I18n.t('selectType'),
link: '/new',
},
{
name: 'events',
label: MailPoet.I18n.t('wooCommerceBreadcrumbsEvents'),
},
{
name: 'conditions',
label: MailPoet.I18n.t('wooCommerceBreadcrumbsConditions'),
},
{
name: 'template',
label: MailPoet.I18n.t('template'),
},
{
name: 'editor',
label: MailPoet.I18n.t('designer'),
},
{
name: 'send',
label: MailPoet.I18n.t('send'),
},
];
return (
<div>
<h1>{MailPoet.I18n.t('wooCommerceSelectEventHeading')}</h1>
<Breadcrumb step="events" steps={steps} />
<ul className="mailpoet_boxes woocommerce clearfix">
{types.map((type, index) => (
<li key={index} data-type={type.id}>
<div>
<div className="mailpoet_thumbnail">
{type.thumbnailImage ? <img src={type.thumbnailImage} /> : null}
</div>
<div className="mailpoet_description">
<div className="title_and_badge">
<h3>{type.title} {type.soon ? `(${MailPoet.I18n.t('wooCommerceEventTitleSoon')})` : ''}</h3>
{type.badge ? <span className={`mailpoet_badge mailpoet_badge_${type.badge.style}`}>{type.badge.text}</span> : ''}
</div>
<p>{type.description}</p>
</div>
<div className="mailpoet_actions">
<a href="?page=mailpoet-premium" target="_blank">
{MailPoet.I18n.t('wooCommercePremiumFeatureLink')}
</a>
</div>
</div>
</li>
), this)}
</ul>
</div>
);
},
});
return WooCommerceAutomaticEmail;
}
);