Subscriber & Segment listings
- fixed filters - added load/save state from url - added goBack on forms in order to get back listing states wx# Please enter the commit message for your changes. Lines starting
This commit is contained in:
@ -1,4 +1,4 @@
|
|||||||
.mailpoet_listing_loading tbody tr,
|
.mailpoet_listing_loading tbody tr
|
||||||
.mailpoet_form_loading tbody tr
|
.mailpoet_form_loading tbody tr
|
||||||
opacity: 0.2
|
opacity: 0.2
|
||||||
|
|
||||||
@ -8,6 +8,20 @@
|
|||||||
.mailpoet_select_all td
|
.mailpoet_select_all td
|
||||||
text-align: center
|
text-align: center
|
||||||
|
|
||||||
table.widefat thead .check-column,
|
.mailpoet_listing_table
|
||||||
table.widefat tfoot .check-column
|
th span
|
||||||
padding: 10px 0 0 3px
|
white-space: nowrap
|
||||||
|
|
||||||
|
thead .check-column
|
||||||
|
tfoot .check-column
|
||||||
|
padding: 10px 0 0 3px
|
||||||
|
|
||||||
|
thead th.column-primary
|
||||||
|
tfoot th.column-primary
|
||||||
|
width: 25em
|
||||||
|
|
||||||
|
// responsive
|
||||||
|
@media screen and (max-width: 782px)
|
||||||
|
thead th.column-primary
|
||||||
|
tfoot th.column-primary
|
||||||
|
width: 100%
|
@ -89,7 +89,12 @@ define(
|
|||||||
this.setState({ loading: false });
|
this.setState({ loading: false });
|
||||||
|
|
||||||
if(response === true) {
|
if(response === true) {
|
||||||
this.history.pushState(null, '/');
|
if(this.props.onSuccess !== undefined) {
|
||||||
|
this.props.onSuccess()
|
||||||
|
} else {
|
||||||
|
this.history.pushState(null, '/')
|
||||||
|
}
|
||||||
|
|
||||||
if(this.props.params.id !== undefined) {
|
if(this.props.params.id !== undefined) {
|
||||||
this.props.messages['updated']();
|
this.props.messages['updated']();
|
||||||
} else {
|
} else {
|
||||||
|
@ -1,65 +1,64 @@
|
|||||||
define([
|
define([
|
||||||
'react'
|
'react',
|
||||||
|
'jquery'
|
||||||
],
|
],
|
||||||
function(
|
function(
|
||||||
React
|
React,
|
||||||
|
jQuery
|
||||||
) {
|
) {
|
||||||
var ListingFilters = React.createClass({
|
var ListingFilters = React.createClass({
|
||||||
handleFilterAction: function() {
|
handleFilterAction: function() {
|
||||||
var filters = this.props.filters;
|
let filters = {}
|
||||||
var selected_filters = Object.keys(filters)
|
this.getAvailableFilters().map((filter, i) => {
|
||||||
.map(function(filter, index) {
|
filters[this.refs['filter-'+i].name] = this.refs['filter-'+i].value
|
||||||
var value = this.refs.filter.value;
|
})
|
||||||
if(value) {
|
return this.props.onSelectFilter(filters);
|
||||||
var output = {};
|
|
||||||
output[filter] = value;
|
|
||||||
return output;
|
|
||||||
}
|
|
||||||
}.bind(this)
|
|
||||||
);
|
|
||||||
return this.props.onSelectFilter(selected_filters);
|
|
||||||
},
|
},
|
||||||
handleChangeAction: function() {
|
getAvailableFilters: function() {
|
||||||
return this.refs.filter.value;
|
let filters = this.props.filters;
|
||||||
|
|
||||||
|
return Object.keys(filters).filter(function(filter) {
|
||||||
|
return !(
|
||||||
|
filters[filter].length === 0
|
||||||
|
|| (
|
||||||
|
filters[filter].length === 1
|
||||||
|
&& !filters[filter][0].value
|
||||||
|
)
|
||||||
|
);
|
||||||
|
})
|
||||||
},
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
var filters = this.props.filters;
|
const filters = this.props.filters;
|
||||||
var selected_filters = this.props.filter;
|
const selected_filters = this.props.filter;
|
||||||
|
|
||||||
var available_filters = Object.keys(filters)
|
const available_filters = this.getAvailableFilters()
|
||||||
.filter(function(filter) {
|
|
||||||
return !(
|
|
||||||
filters[filter].length === 0
|
|
||||||
|| (
|
|
||||||
filters[filter].length === 1
|
|
||||||
&& !filters[filter][0].value
|
|
||||||
)
|
|
||||||
);
|
|
||||||
})
|
|
||||||
.map(function(filter, i) {
|
.map(function(filter, i) {
|
||||||
var defaultValue = false;
|
let default_value = false;
|
||||||
if(selected_filters[filter] !== undefined) {
|
if(selected_filters[filter] !== undefined && selected_filters[filter]) {
|
||||||
defaultValue = selected_filters[filter];
|
default_value = selected_filters[filter]
|
||||||
|
} else {
|
||||||
|
jQuery(`select[name="${filter}"]`).val('');
|
||||||
}
|
}
|
||||||
return (
|
return (
|
||||||
<select
|
<select
|
||||||
ref={ 'filter' }
|
ref={ `filter-${i}` }
|
||||||
key={ 'filter-'+i }
|
key={ `filter-${i}` }
|
||||||
defaultValue={ defaultValue }
|
name={ filter }
|
||||||
onChange={ this.handleChangeAction }>
|
defaultValue={ default_value }
|
||||||
{ filters[filter].map(function(option, j) {
|
>
|
||||||
return (
|
{ filters[filter].map(function(option, j) {
|
||||||
<option
|
return (
|
||||||
value={ option.value }
|
<option
|
||||||
key={ 'filter-option-' + j }
|
value={ option.value }
|
||||||
>{ option.label }</option>
|
key={ 'filter-option-' + j }
|
||||||
);
|
>{ option.label }</option>
|
||||||
}.bind(this)) }
|
);
|
||||||
|
}.bind(this)) }
|
||||||
</select>
|
</select>
|
||||||
);
|
);
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
|
|
||||||
var button = false;
|
let button = false;
|
||||||
|
|
||||||
if(available_filters.length > 0) {
|
if(available_filters.length > 0) {
|
||||||
button = (
|
button = (
|
||||||
|
@ -282,43 +282,101 @@ define(
|
|||||||
};
|
};
|
||||||
},
|
},
|
||||||
componentDidUpdate: function(prevProps, prevState) {
|
componentDidUpdate: function(prevProps, prevState) {
|
||||||
// set group to "all" if trash gets emptied
|
// reset group to "all" if trash gets emptied
|
||||||
if(
|
if(
|
||||||
|
// we were viewing the trash
|
||||||
(prevState.group === 'trash' && prevState.count > 0)
|
(prevState.group === 'trash' && prevState.count > 0)
|
||||||
&&
|
&&
|
||||||
|
// we are still viewing the trash but there are no items left
|
||||||
(this.state.group === 'trash' && this.state.count === 0)
|
(this.state.group === 'trash' && this.state.count === 0)
|
||||||
|
&&
|
||||||
|
// only do this when no filter is set
|
||||||
|
(Object.keys(this.state.filter).length === 0)
|
||||||
) {
|
) {
|
||||||
this.handleGroup('all');
|
this.handleGroup('all');
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
getParam: function(param) {
|
||||||
|
var regex = /(.*)\[(.*)\]/
|
||||||
|
var matches = regex.exec(param)
|
||||||
|
return [matches[1], matches[2]]
|
||||||
|
},
|
||||||
|
initWithParams: function(params) {
|
||||||
|
let state = this.state || {}
|
||||||
|
let original_state = state
|
||||||
|
// check for url params
|
||||||
|
if(params.splat !== undefined) {
|
||||||
|
params.splat.split('/').map(param => {
|
||||||
|
let [key, value] = this.getParam(param);
|
||||||
|
switch(key) {
|
||||||
|
case 'filter':
|
||||||
|
let filters = {}
|
||||||
|
value.split('&').map(function(pair) {
|
||||||
|
let [k, v] = pair.split('=')
|
||||||
|
filters[k] = v
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
state.filter = filters
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
state[key] = value
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
if(this.props.limit !== undefined) {
|
||||||
|
state.limit = Math.abs(~~this.props.limit);
|
||||||
|
}
|
||||||
|
this.setState(state, function() {
|
||||||
|
this.getItems();
|
||||||
|
}.bind(this));
|
||||||
|
},
|
||||||
|
setParams: function() {
|
||||||
|
var params = Object.keys(this.state)
|
||||||
|
.filter(key => {
|
||||||
|
return (
|
||||||
|
[
|
||||||
|
'group',
|
||||||
|
'filter',
|
||||||
|
'search',
|
||||||
|
'page',
|
||||||
|
'sort_by',
|
||||||
|
'sort_order'
|
||||||
|
].indexOf(key) !== -1
|
||||||
|
)
|
||||||
|
})
|
||||||
|
.map(key => {
|
||||||
|
let value = this.state[key]
|
||||||
|
if(value === Object(value)) {
|
||||||
|
value = jQuery.param(value)
|
||||||
|
} else if(value === Boolean(value)) {
|
||||||
|
value = value.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
if(value !== '') {
|
||||||
|
return `${key}[${value}]`
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.filter(key => { return (key !== undefined) })
|
||||||
|
.join('/');
|
||||||
|
params = '/'+params
|
||||||
|
|
||||||
|
if(this.props.location) {
|
||||||
|
if(this.props.location.pathname !== params) {
|
||||||
|
this.history.pushState(null, `${params}`)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
componentDidMount: function() {
|
componentDidMount: function() {
|
||||||
if(this.isMounted()) {
|
if(this.isMounted()) {
|
||||||
var state = this.state || {};
|
const params = this.props.params || {}
|
||||||
var params = this.props.params || {};
|
this.initWithParams(params)
|
||||||
|
|
||||||
// set filters
|
|
||||||
if(params.filter !== undefined) {
|
|
||||||
var filter = {};
|
|
||||||
var pairs = params.filter
|
|
||||||
.split('&')
|
|
||||||
.map(function(pair) {
|
|
||||||
var [key, value] = pair.split('=');
|
|
||||||
filter[key] = value;
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
state.filter = filter;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(this.props.limit !== undefined) {
|
|
||||||
state.limit = Math.abs(~~this.props.limit);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.setState(state, function() {
|
|
||||||
this.getItems();
|
|
||||||
}.bind(this));
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
componentWillReceiveProps: function(nextProps) {
|
||||||
|
const params = nextProps.params || {}
|
||||||
|
//this.initWithParams(params)
|
||||||
|
},
|
||||||
getItems: function() {
|
getItems: function() {
|
||||||
if(this.isMounted()) {
|
if(this.isMounted()) {
|
||||||
this.setState({ loading: true });
|
this.setState({ loading: true });
|
||||||
@ -443,6 +501,7 @@ define(
|
|||||||
selection: false,
|
selection: false,
|
||||||
selected_ids: []
|
selected_ids: []
|
||||||
}, function() {
|
}, function() {
|
||||||
|
this.setParams();
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -451,6 +510,7 @@ define(
|
|||||||
sort_by: sort_by,
|
sort_by: sort_by,
|
||||||
sort_order: sort_order,
|
sort_order: sort_order,
|
||||||
}, function() {
|
}, function() {
|
||||||
|
this.setParams();
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -510,6 +570,7 @@ define(
|
|||||||
filter: filters,
|
filter: filters,
|
||||||
page: 1
|
page: 1
|
||||||
}, function() {
|
}, function() {
|
||||||
|
this.setParams();
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -519,10 +580,11 @@ define(
|
|||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
group: group,
|
group: group,
|
||||||
filter: [],
|
filter: {},
|
||||||
search: '',
|
search: '',
|
||||||
page: 1
|
page: 1
|
||||||
}, function() {
|
}, function() {
|
||||||
|
this.setParams();
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -532,6 +594,7 @@ define(
|
|||||||
selection: false,
|
selection: false,
|
||||||
selected_ids: []
|
selected_ids: []
|
||||||
}, function() {
|
}, function() {
|
||||||
|
this.setParams();
|
||||||
this.getItems();
|
this.getItems();
|
||||||
}.bind(this));
|
}.bind(this));
|
||||||
},
|
},
|
||||||
@ -571,7 +634,8 @@ define(
|
|||||||
// item actions
|
// item actions
|
||||||
var item_actions = this.props.item_actions || [];
|
var item_actions = this.props.item_actions || [];
|
||||||
|
|
||||||
var tableClasses = classNames(
|
var table_classes = classNames(
|
||||||
|
'mailpoet_listing_table',
|
||||||
'wp-list-table',
|
'wp-list-table',
|
||||||
'widefat',
|
'widefat',
|
||||||
'fixed',
|
'fixed',
|
||||||
@ -622,7 +686,7 @@ define(
|
|||||||
limit={ this.state.limit }
|
limit={ this.state.limit }
|
||||||
onSetPage={ this.handleSetPage } />
|
onSetPage={ this.handleSetPage } />
|
||||||
</div>
|
</div>
|
||||||
<table className={ tableClasses }>
|
<table className={ table_classes }>
|
||||||
<thead>
|
<thead>
|
||||||
<ListingHeader
|
<ListingHeader
|
||||||
onSort={ this.handleSort }
|
onSort={ this.handleSort }
|
||||||
|
@ -104,6 +104,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
|||||||
pagination = (
|
pagination = (
|
||||||
<span className="pagination-links">
|
<span className="pagination-links">
|
||||||
{firstPage}
|
{firstPage}
|
||||||
|
|
||||||
{previousPage}
|
{previousPage}
|
||||||
|
|
||||||
<span className="paging-input">
|
<span className="paging-input">
|
||||||
@ -128,6 +129,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
|||||||
</span>
|
</span>
|
||||||
|
|
||||||
{nextPage}
|
{nextPage}
|
||||||
|
|
||||||
{lastPage}
|
{lastPage}
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
@ -140,7 +142,7 @@ define(['react', 'classnames'], function(React, classNames) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={ classes }>
|
<div className={ classes }>
|
||||||
<span className="displaying-num">{ this.props.count } item(s)</span>
|
<span className="displaying-num">{ this.props.count } items</span>
|
||||||
{ pagination }
|
{ pagination }
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
@ -7,6 +7,9 @@ define(['react'], function(React) {
|
|||||||
this.refs.search.value
|
this.refs.search.value
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
componentWillReceiveProps: function(nextProps) {
|
||||||
|
this.refs.search.value = nextProps.search
|
||||||
|
},
|
||||||
render: function() {
|
render: function() {
|
||||||
if(this.props.search === false) {
|
if(this.props.search === false) {
|
||||||
return false;
|
return false;
|
||||||
|
@ -163,6 +163,7 @@ define(
|
|||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<Listing
|
<Listing
|
||||||
|
params={ this.props.params }
|
||||||
endpoint="newsletters"
|
endpoint="newsletters"
|
||||||
onRenderItem={this.renderItem}
|
onRenderItem={this.renderItem}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
@ -34,21 +34,27 @@ define(
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
var Link = Router.Link;
|
|
||||||
|
|
||||||
var SegmentForm = React.createClass({
|
var SegmentForm = React.createClass({
|
||||||
|
mixins: [
|
||||||
|
Router.History
|
||||||
|
],
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="title">
|
<h2 className="title">
|
||||||
Segment <Link className="add-new-h2" to="/">Back to list</Link>
|
Segment <a
|
||||||
|
href="javascript:;"
|
||||||
|
className="add-new-h2"
|
||||||
|
onClick={ this.history.goBack }
|
||||||
|
>Back to list</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
endpoint="segments"
|
endpoint="segments"
|
||||||
fields={ fields }
|
fields={ fields }
|
||||||
params={ this.props.params }
|
params={ this.props.params }
|
||||||
messages={ messages } />
|
messages={ messages }
|
||||||
|
onSuccess={ this.history.goBack } />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,222 +1,213 @@
|
|||||||
define(
|
import React from 'react'
|
||||||
[
|
import { Router, Route, Link } from 'react-router'
|
||||||
'react',
|
|
||||||
'react-router',
|
|
||||||
'listing/listing.jsx',
|
|
||||||
'classnames',
|
|
||||||
'mailpoet'
|
|
||||||
],
|
|
||||||
function(
|
|
||||||
React,
|
|
||||||
Router,
|
|
||||||
Listing,
|
|
||||||
classNames,
|
|
||||||
MailPoet
|
|
||||||
) {
|
|
||||||
var columns = [
|
|
||||||
{
|
|
||||||
name: 'name',
|
|
||||||
label: 'Name',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'description',
|
|
||||||
label: 'Description',
|
|
||||||
sortable: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'subscribed',
|
|
||||||
label: 'Subscribed',
|
|
||||||
sortable: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'unconfirmed',
|
|
||||||
label: 'Unconfirmed',
|
|
||||||
sortable: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'unsubscribed',
|
|
||||||
label: 'Unsubscribed',
|
|
||||||
sortable: false
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'created_at',
|
|
||||||
label: 'Created on',
|
|
||||||
sortable: true
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var messages = {
|
import jQuery from 'jquery'
|
||||||
onDelete: function(response) {
|
import MailPoet from 'mailpoet'
|
||||||
var count = ~~response.segments;
|
import classNames from 'classnames'
|
||||||
var message = null;
|
|
||||||
|
|
||||||
if(count === 1 || response === true) {
|
import Listing from 'listing/listing.jsx'
|
||||||
message = (
|
|
||||||
'1 segment was moved to the trash.'
|
|
||||||
);
|
|
||||||
} else if(count > 1) {
|
|
||||||
message = (
|
|
||||||
'%$1d segments were moved to the trash.'
|
|
||||||
).replace('%$1d', count);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(message !== null) {
|
var columns = [
|
||||||
MailPoet.Notice.success(message);
|
{
|
||||||
}
|
name: 'name',
|
||||||
},
|
label: 'Name',
|
||||||
onConfirmDelete: function(response) {
|
sortable: true
|
||||||
var count = ~~response.segments;
|
},
|
||||||
var message = null;
|
{
|
||||||
|
name: 'description',
|
||||||
if(count === 1 || response === true) {
|
label: 'Description',
|
||||||
message = (
|
sortable: false
|
||||||
'1 segment was permanently deleted.'
|
},
|
||||||
);
|
{
|
||||||
} else if(count > 1) {
|
name: 'subscribed',
|
||||||
message = (
|
label: 'Subscribed',
|
||||||
'%$1d segments were permanently deleted.'
|
sortable: false
|
||||||
).replace('%$1d', count);
|
},
|
||||||
}
|
{
|
||||||
|
name: 'unconfirmed',
|
||||||
if(message !== null) {
|
label: 'Unconfirmed',
|
||||||
MailPoet.Notice.success(message);
|
sortable: false
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
onRestore: function(response) {
|
name: 'unsubscribed',
|
||||||
var count = ~~response.segments;
|
label: 'Unsubscribed',
|
||||||
var message = null;
|
sortable: false
|
||||||
|
},
|
||||||
if(count === 1 || response === true) {
|
{
|
||||||
message = (
|
name: 'created_at',
|
||||||
'1 segment has been restored from the trash.'
|
label: 'Created on',
|
||||||
);
|
sortable: true
|
||||||
} else if(count > 1) {
|
|
||||||
message = (
|
|
||||||
'%$1d segments have been restored from the trash.'
|
|
||||||
).replace('%$1d', count);
|
|
||||||
}
|
|
||||||
|
|
||||||
if(message !== null) {
|
|
||||||
MailPoet.Notice.success(message);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var Link = Router.Link;
|
|
||||||
var item_actions = [
|
|
||||||
{
|
|
||||||
name: 'edit',
|
|
||||||
link: function(item) {
|
|
||||||
return (
|
|
||||||
<Link to={ `/edit/${item.id}` }>Edit</Link>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'duplicate_segment',
|
|
||||||
refresh: true,
|
|
||||||
link: function(item) {
|
|
||||||
return (
|
|
||||||
<a
|
|
||||||
href="javascript:;"
|
|
||||||
onClick={ this.onDuplicate.bind(null, item) }
|
|
||||||
>Duplicate</a>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
onDuplicate: function(item) {
|
|
||||||
MailPoet.Ajax.post({
|
|
||||||
endpoint: 'segments',
|
|
||||||
action: 'duplicate',
|
|
||||||
data: item.id
|
|
||||||
}).done(function() {
|
|
||||||
MailPoet.Notice.success(
|
|
||||||
('List "%$1s" has been duplicated.').replace('%$1s', item.name)
|
|
||||||
);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'view_subscribers',
|
|
||||||
link: function(item) {
|
|
||||||
return (
|
|
||||||
<a href={ item.subscribers_url }>View subscribers</a>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var bulk_actions = [
|
|
||||||
{
|
|
||||||
name: 'trash',
|
|
||||||
label: 'Trash',
|
|
||||||
getData: function() {
|
|
||||||
return {
|
|
||||||
confirm: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess: messages.onDelete
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var Link = Router.Link;
|
|
||||||
|
|
||||||
var SegmentList = React.createClass({
|
|
||||||
renderItem: function(segment, actions) {
|
|
||||||
var rowClasses = classNames(
|
|
||||||
'manage-column',
|
|
||||||
'column-primary',
|
|
||||||
'has-row-actions'
|
|
||||||
);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<td className={ rowClasses }>
|
|
||||||
<strong>
|
|
||||||
<a>{ segment.name }</a>
|
|
||||||
</strong>
|
|
||||||
{ actions }
|
|
||||||
</td>
|
|
||||||
<td className="column-date" data-colname="Description">
|
|
||||||
<abbr>{ segment.description }</abbr>
|
|
||||||
</td>
|
|
||||||
<td className="column-date" data-colname="Subscribed">
|
|
||||||
<abbr>{ segment.subscribed || 0 }</abbr>
|
|
||||||
</td>
|
|
||||||
<td className="column-date" data-colname="Unconfirmed">
|
|
||||||
<abbr>{ segment.unconfirmed || 0 }</abbr>
|
|
||||||
</td>
|
|
||||||
<td className="column-date" data-colname="Unsubscribed">
|
|
||||||
<abbr>{ segment.unsubscribed || 0 }</abbr>
|
|
||||||
</td>
|
|
||||||
<td className="column-date" data-colname="Created on">
|
|
||||||
<abbr>{ segment.created_at }</abbr>
|
|
||||||
</td>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2 className="title">
|
|
||||||
Segments <Link className="add-new-h2" to="/new">New</Link>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<Listing
|
|
||||||
messages={ messages }
|
|
||||||
search={ false }
|
|
||||||
limit={ 1000 }
|
|
||||||
endpoint="segments"
|
|
||||||
onRenderItem={ this.renderItem }
|
|
||||||
columns={ columns }
|
|
||||||
bulk_actions={ bulk_actions }
|
|
||||||
item_actions={ item_actions }
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return SegmentList;
|
|
||||||
}
|
}
|
||||||
);
|
];
|
||||||
|
|
||||||
|
var messages = {
|
||||||
|
onDelete: function(response) {
|
||||||
|
var count = ~~response.segments;
|
||||||
|
var message = null;
|
||||||
|
|
||||||
|
if(count === 1 || response === true) {
|
||||||
|
message = (
|
||||||
|
'1 segment was moved to the trash.'
|
||||||
|
);
|
||||||
|
} else if(count > 1) {
|
||||||
|
message = (
|
||||||
|
'%$1d segments were moved to the trash.'
|
||||||
|
).replace('%$1d', count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message !== null) {
|
||||||
|
MailPoet.Notice.success(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onConfirmDelete: function(response) {
|
||||||
|
var count = ~~response.segments;
|
||||||
|
var message = null;
|
||||||
|
|
||||||
|
if(count === 1 || response === true) {
|
||||||
|
message = (
|
||||||
|
'1 segment was permanently deleted.'
|
||||||
|
);
|
||||||
|
} else if(count > 1) {
|
||||||
|
message = (
|
||||||
|
'%$1d segments were permanently deleted.'
|
||||||
|
).replace('%$1d', count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message !== null) {
|
||||||
|
MailPoet.Notice.success(message);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onRestore: function(response) {
|
||||||
|
var count = ~~response.segments;
|
||||||
|
var message = null;
|
||||||
|
|
||||||
|
if(count === 1 || response === true) {
|
||||||
|
message = (
|
||||||
|
'1 segment has been restored from the trash.'
|
||||||
|
);
|
||||||
|
} else if(count > 1) {
|
||||||
|
message = (
|
||||||
|
'%$1d segments have been restored from the trash.'
|
||||||
|
).replace('%$1d', count);
|
||||||
|
}
|
||||||
|
|
||||||
|
if(message !== null) {
|
||||||
|
MailPoet.Notice.success(message);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var item_actions = [
|
||||||
|
{
|
||||||
|
name: 'edit',
|
||||||
|
link: function(item) {
|
||||||
|
return (
|
||||||
|
<Link to={ `/edit/${item.id}` }>Edit</Link>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'duplicate_segment',
|
||||||
|
refresh: true,
|
||||||
|
link: function(item) {
|
||||||
|
return (
|
||||||
|
<a
|
||||||
|
href="javascript:;"
|
||||||
|
onClick={ this.onDuplicate.bind(null, item) }
|
||||||
|
>Duplicate</a>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
onDuplicate: function(item) {
|
||||||
|
MailPoet.Ajax.post({
|
||||||
|
endpoint: 'segments',
|
||||||
|
action: 'duplicate',
|
||||||
|
data: item.id
|
||||||
|
}).done(function() {
|
||||||
|
MailPoet.Notice.success(
|
||||||
|
('List "%$1s" has been duplicated.').replace('%$1s', item.name)
|
||||||
|
);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'view_subscribers',
|
||||||
|
link: function(item) {
|
||||||
|
return (
|
||||||
|
<a href={ item.subscribers_url }>View subscribers</a>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var bulk_actions = [
|
||||||
|
{
|
||||||
|
name: 'trash',
|
||||||
|
label: 'Trash',
|
||||||
|
getData: function() {
|
||||||
|
return {
|
||||||
|
confirm: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess: messages.onDelete
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
var SegmentList = React.createClass({
|
||||||
|
renderItem: function(segment, actions) {
|
||||||
|
var rowClasses = classNames(
|
||||||
|
'manage-column',
|
||||||
|
'column-primary',
|
||||||
|
'has-row-actions'
|
||||||
|
);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<td className={ rowClasses }>
|
||||||
|
<strong>
|
||||||
|
<a>{ segment.name }</a>
|
||||||
|
</strong>
|
||||||
|
{ actions }
|
||||||
|
</td>
|
||||||
|
<td className="column-date" data-colname="Description">
|
||||||
|
<abbr>{ segment.description }</abbr>
|
||||||
|
</td>
|
||||||
|
<td className="column-date" data-colname="Subscribed">
|
||||||
|
<abbr>{ segment.subscribed || 0 }</abbr>
|
||||||
|
</td>
|
||||||
|
<td className="column-date" data-colname="Unconfirmed">
|
||||||
|
<abbr>{ segment.unconfirmed || 0 }</abbr>
|
||||||
|
</td>
|
||||||
|
<td className="column-date" data-colname="Unsubscribed">
|
||||||
|
<abbr>{ segment.unsubscribed || 0 }</abbr>
|
||||||
|
</td>
|
||||||
|
<td className="column-date" data-colname="Created on">
|
||||||
|
<abbr>{ segment.created_at }</abbr>
|
||||||
|
</td>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2 className="title">
|
||||||
|
Segments <Link className="add-new-h2" to="/new">New</Link>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<Listing
|
||||||
|
location={ this.props.location }
|
||||||
|
params={ this.props.params }
|
||||||
|
messages={ messages }
|
||||||
|
search={ false }
|
||||||
|
limit={ 1000 }
|
||||||
|
endpoint="segments"
|
||||||
|
onRenderItem={ this.renderItem }
|
||||||
|
columns={ columns }
|
||||||
|
bulk_actions={ bulk_actions }
|
||||||
|
item_actions={ item_actions }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = SegmentList;
|
@ -52,18 +52,26 @@ define(
|
|||||||
var Link = Router.Link;
|
var Link = Router.Link;
|
||||||
|
|
||||||
var SubscriberForm = React.createClass({
|
var SubscriberForm = React.createClass({
|
||||||
|
mixins: [
|
||||||
|
Router.History
|
||||||
|
],
|
||||||
render: function() {
|
render: function() {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<h2 className="title">
|
<h2 className="title">
|
||||||
Subscriber <Link className="add-new-h2" to="/">Back to list</Link>
|
Subscriber <a
|
||||||
|
href="javascript:;"
|
||||||
|
className="add-new-h2"
|
||||||
|
onClick={ this.history.goBack }
|
||||||
|
>Back to list</a>
|
||||||
</h2>
|
</h2>
|
||||||
|
|
||||||
<Form
|
<Form
|
||||||
endpoint="subscribers"
|
endpoint="subscribers"
|
||||||
fields={ fields }
|
fields={ fields }
|
||||||
params={ this.props.params }
|
params={ this.props.params }
|
||||||
messages={ messages } />
|
messages={ messages }
|
||||||
|
onSuccess={ this.history.goBack } />
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,327 +1,300 @@
|
|||||||
define(
|
import React from 'react'
|
||||||
[
|
import { Router, Route, Link } from 'react-router'
|
||||||
'react',
|
|
||||||
'react-router',
|
|
||||||
'listing/listing.jsx',
|
|
||||||
'form/fields/selection.jsx',
|
|
||||||
'classnames',
|
|
||||||
'mailpoet',
|
|
||||||
'jquery',
|
|
||||||
'select2'
|
|
||||||
],
|
|
||||||
function(
|
|
||||||
React,
|
|
||||||
Router,
|
|
||||||
Listing,
|
|
||||||
Selection,
|
|
||||||
classNames,
|
|
||||||
MailPoet,
|
|
||||||
jQuery
|
|
||||||
) {
|
|
||||||
var Link = Router.Link;
|
|
||||||
|
|
||||||
var columns = [
|
import jQuery from 'jquery'
|
||||||
{
|
import MailPoet from 'mailpoet'
|
||||||
name: 'email',
|
import classNames from 'classnames'
|
||||||
label: 'Email',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'first_name',
|
|
||||||
label: 'Firstname',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'last_name',
|
|
||||||
label: 'Lastname',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'status',
|
|
||||||
label: 'Status',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'segments',
|
|
||||||
label: 'Lists',
|
|
||||||
sortable: false
|
|
||||||
},
|
|
||||||
|
|
||||||
{
|
import Listing from 'listing/listing.jsx'
|
||||||
name: 'created_at',
|
import Selection from 'form/fields/selection.jsx'
|
||||||
label: 'Subscribed on',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'updated_at',
|
|
||||||
label: 'Last modified on',
|
|
||||||
sortable: true
|
|
||||||
},
|
|
||||||
];
|
|
||||||
|
|
||||||
var messages = {
|
const columns = [
|
||||||
onDelete: function(response) {
|
{
|
||||||
var count = ~~response.subscribers;
|
name: 'email',
|
||||||
var message = null;
|
label: 'Subscriber',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'status',
|
||||||
|
label: 'Status',
|
||||||
|
sortable: true
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'segments',
|
||||||
|
label: 'Lists',
|
||||||
|
sortable: false
|
||||||
|
},
|
||||||
|
|
||||||
if(count === 1) {
|
{
|
||||||
message = (
|
name: 'created_at',
|
||||||
'1 subscriber was moved to the trash.'
|
label: 'Subscribed on',
|
||||||
).replace('%$1d', count);
|
sortable: true
|
||||||
} else if(count > 1) {
|
},
|
||||||
message = (
|
{
|
||||||
'%$1d subscribers were moved to the trash.'
|
name: 'updated_at',
|
||||||
).replace('%$1d', count);
|
label: 'Last modified on',
|
||||||
}
|
sortable: true
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
if(message !== null) {
|
const messages = {
|
||||||
MailPoet.Notice.success(message);
|
onDelete: function(response) {
|
||||||
}
|
let count = ~~response.subscribers;
|
||||||
},
|
let message = null;
|
||||||
onConfirmDelete: function(response) {
|
|
||||||
var count = ~~response.subscribers;
|
|
||||||
var message = null;
|
|
||||||
|
|
||||||
if(count === 1) {
|
if(count === 1) {
|
||||||
message = (
|
message = (
|
||||||
'1 subscriber was permanently deleted.'
|
'1 subscriber was moved to the trash.'
|
||||||
).replace('%$1d', count);
|
).replace('%$1d', count);
|
||||||
} else if(count > 1) {
|
} else if(count > 1) {
|
||||||
message = (
|
message = (
|
||||||
'%$1d subscribers were permanently deleted.'
|
'%$1d subscribers were moved to the trash.'
|
||||||
).replace('%$1d', count);
|
).replace('%$1d', count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(message !== null) {
|
if(message !== null) {
|
||||||
MailPoet.Notice.success(message);
|
MailPoet.Notice.success(message);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
onRestore: function(response) {
|
onConfirmDelete: function(response) {
|
||||||
var count = ~~response.subscribers;
|
let count = ~~response.subscribers;
|
||||||
var message = null;
|
let message = null;
|
||||||
|
|
||||||
if(count === 1) {
|
if(count === 1) {
|
||||||
message = (
|
message = (
|
||||||
'1 subscriber has been restored from the trash.'
|
'1 subscriber was permanently deleted.'
|
||||||
).replace('%$1d', count);
|
).replace('%$1d', count);
|
||||||
} else if(count > 1) {
|
} else if(count > 1) {
|
||||||
message = (
|
message = (
|
||||||
'%$1d subscribers have been restored from the trash.'
|
'%$1d subscribers were permanently deleted.'
|
||||||
).replace('%$1d', count);
|
).replace('%$1d', count);
|
||||||
}
|
}
|
||||||
|
|
||||||
if(message !== null) {
|
if(message !== null) {
|
||||||
MailPoet.Notice.success(message);
|
MailPoet.Notice.success(message);
|
||||||
}
|
}
|
||||||
}
|
},
|
||||||
};
|
onRestore: function(response) {
|
||||||
|
let count = ~~response.subscribers;
|
||||||
|
let message = null;
|
||||||
|
|
||||||
var bulk_actions = [
|
if(count === 1) {
|
||||||
{
|
message = (
|
||||||
name: 'moveToList',
|
'1 subscriber has been restored from the trash.'
|
||||||
label: 'Move to list...',
|
).replace('%$1d', count);
|
||||||
onSelect: function() {
|
} else if(count > 1) {
|
||||||
var field = {
|
message = (
|
||||||
id: 'move_to_segment',
|
'%$1d subscribers have been restored from the trash.'
|
||||||
endpoint: 'segments'
|
).replace('%$1d', count);
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
if(message !== null) {
|
||||||
<Selection field={ field }/>
|
MailPoet.Notice.success(message);
|
||||||
);
|
}
|
||||||
},
|
|
||||||
getData: function() {
|
|
||||||
return {
|
|
||||||
segment_id: ~~(jQuery('#move_to_segment').val())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess: function(response) {
|
|
||||||
MailPoet.Notice.success(
|
|
||||||
'%$1d subscribers were moved to list <strong>%$2s</strong>.'
|
|
||||||
.replace('%$1d', ~~response.subscribers)
|
|
||||||
.replace('%$2s', response.segment)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'addToList',
|
|
||||||
label: 'Add to list...',
|
|
||||||
onSelect: function() {
|
|
||||||
var field = {
|
|
||||||
id: 'add_to_segment',
|
|
||||||
endpoint: 'segments'
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Selection field={ field }/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
getData: function() {
|
|
||||||
return {
|
|
||||||
segment_id: ~~(jQuery('#add_to_segment').val())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess: function(response) {
|
|
||||||
MailPoet.Notice.success(
|
|
||||||
'%$1d subscribers were added to list <strong>%$2s</strong>.'
|
|
||||||
.replace('%$1d', ~~response.subscribers)
|
|
||||||
.replace('%$2s', response.segment)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'removeFromList',
|
|
||||||
label: 'Remove from list...',
|
|
||||||
onSelect: function() {
|
|
||||||
var field = {
|
|
||||||
id: 'remove_from_segment',
|
|
||||||
endpoint: 'segments'
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Selection field={ field }/>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
getData: function() {
|
|
||||||
return {
|
|
||||||
segment_id: ~~(jQuery('#remove_from_segment').val())
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess: function(response) {
|
|
||||||
MailPoet.Notice.success(
|
|
||||||
'%$1d subscribers were removed from list <strong>%$2s</strong>.'
|
|
||||||
.replace('%$1d', ~~response.subscribers)
|
|
||||||
.replace('%$2s', response.segment)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'removeFromAllLists',
|
|
||||||
label: 'Remove from all lists',
|
|
||||||
onSuccess: function(response) {
|
|
||||||
MailPoet.Notice.success(
|
|
||||||
'%$1d subscribers were removed from all lists.'
|
|
||||||
.replace('%$1d', ~~response.subscribers)
|
|
||||||
.replace('%$2s', response.segment)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'confirmUnconfirmed',
|
|
||||||
label: 'Confirm unconfirmed',
|
|
||||||
onSuccess: function(response) {
|
|
||||||
MailPoet.Notice.success(
|
|
||||||
'%$1d subscribers have been confirmed.'
|
|
||||||
.replace('%$1d', ~~response.subscribers)
|
|
||||||
);
|
|
||||||
}
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: 'trash',
|
|
||||||
label: 'Trash',
|
|
||||||
getData: function() {
|
|
||||||
return {
|
|
||||||
confirm: false
|
|
||||||
}
|
|
||||||
},
|
|
||||||
onSuccess: messages.onDelete
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
var SubscriberList = React.createClass({
|
|
||||||
renderItem: function(subscriber, actions) {
|
|
||||||
var row_classes = classNames(
|
|
||||||
'manage-column',
|
|
||||||
'column-primary',
|
|
||||||
'has-row-actions',
|
|
||||||
'column-username'
|
|
||||||
);
|
|
||||||
|
|
||||||
var status = '';
|
|
||||||
|
|
||||||
switch(subscriber.status) {
|
|
||||||
case 'subscribed':
|
|
||||||
status = 'Subscribed';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'unconfirmed':
|
|
||||||
status = 'Unconfirmed';
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 'unsubscribed':
|
|
||||||
status = 'Unsubscribed';
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
var segments = mailpoet_segments.filter(function(segment) {
|
|
||||||
return (jQuery.inArray(segment.id, subscriber.segments) !== -1);
|
|
||||||
}).map(function(segment) {
|
|
||||||
return segment.name;
|
|
||||||
}).join(', ');
|
|
||||||
|
|
||||||
var avatar = false;
|
|
||||||
if(subscriber.avatar_url) {
|
|
||||||
avatar = (
|
|
||||||
<img
|
|
||||||
className="avatar"
|
|
||||||
src={ subscriber.avatar_url }
|
|
||||||
title=""
|
|
||||||
width="32"
|
|
||||||
height="32"
|
|
||||||
/>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<td className={ row_classes }>
|
|
||||||
{ avatar }
|
|
||||||
<strong><Link to={ `/edit/${ subscriber.id }` }>
|
|
||||||
{ subscriber.email }
|
|
||||||
</Link></strong>
|
|
||||||
{ actions }
|
|
||||||
</td>
|
|
||||||
<td className="column" data-colname="First name">
|
|
||||||
{ subscriber.first_name }
|
|
||||||
</td>
|
|
||||||
<td className="column" data-colname="Last name">
|
|
||||||
{ subscriber.last_name }
|
|
||||||
</td>
|
|
||||||
<td className="column" data-colname="Status">
|
|
||||||
{ status }
|
|
||||||
</td>
|
|
||||||
<td className="column" data-colname="Lists">
|
|
||||||
{ segments }
|
|
||||||
</td>
|
|
||||||
<td className="column-date" data-colname="Subscribed on">
|
|
||||||
<abbr>{ subscriber.created_at }</abbr>
|
|
||||||
</td>
|
|
||||||
<td className="column-date" data-colname="Last modified on">
|
|
||||||
<abbr>{ subscriber.updated_at }</abbr>
|
|
||||||
</td>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
},
|
|
||||||
render: function() {
|
|
||||||
return (
|
|
||||||
<div>
|
|
||||||
<h2 className="title">
|
|
||||||
Subscribers <Link className="add-new-h2" to="/new">New</Link>
|
|
||||||
</h2>
|
|
||||||
|
|
||||||
<Listing
|
|
||||||
params={ this.props.params }
|
|
||||||
endpoint="subscribers"
|
|
||||||
onRenderItem={ this.renderItem }
|
|
||||||
columns={ columns }
|
|
||||||
bulk_actions={ bulk_actions }
|
|
||||||
messages={ messages }
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return SubscriberList;
|
|
||||||
}
|
}
|
||||||
);
|
};
|
||||||
|
|
||||||
|
const bulk_actions = [
|
||||||
|
{
|
||||||
|
name: 'moveToList',
|
||||||
|
label: 'Move to list...',
|
||||||
|
onSelect: function() {
|
||||||
|
let field = {
|
||||||
|
id: 'move_to_segment',
|
||||||
|
endpoint: 'segments'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Selection field={ field }/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getData: function() {
|
||||||
|
return {
|
||||||
|
segment_id: ~~(jQuery('#move_to_segment').val())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess: function(response) {
|
||||||
|
MailPoet.Notice.success(
|
||||||
|
'%$1d subscribers were moved to list <strong>%$2s</strong>.'
|
||||||
|
.replace('%$1d', ~~response.subscribers)
|
||||||
|
.replace('%$2s', response.segment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'addToList',
|
||||||
|
label: 'Add to list...',
|
||||||
|
onSelect: function() {
|
||||||
|
let field = {
|
||||||
|
id: 'add_to_segment',
|
||||||
|
endpoint: 'segments'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Selection field={ field }/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getData: function() {
|
||||||
|
return {
|
||||||
|
segment_id: ~~(jQuery('#add_to_segment').val())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess: function(response) {
|
||||||
|
MailPoet.Notice.success(
|
||||||
|
'%$1d subscribers were added to list <strong>%$2s</strong>.'
|
||||||
|
.replace('%$1d', ~~response.subscribers)
|
||||||
|
.replace('%$2s', response.segment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'removeFromList',
|
||||||
|
label: 'Remove from list...',
|
||||||
|
onSelect: function() {
|
||||||
|
let field = {
|
||||||
|
id: 'remove_from_segment',
|
||||||
|
endpoint: 'segments'
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Selection field={ field }/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
getData: function() {
|
||||||
|
return {
|
||||||
|
segment_id: ~~(jQuery('#remove_from_segment').val())
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess: function(response) {
|
||||||
|
MailPoet.Notice.success(
|
||||||
|
'%$1d subscribers were removed from list <strong>%$2s</strong>.'
|
||||||
|
.replace('%$1d', ~~response.subscribers)
|
||||||
|
.replace('%$2s', response.segment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'removeFromAllLists',
|
||||||
|
label: 'Remove from all lists',
|
||||||
|
onSuccess: function(response) {
|
||||||
|
MailPoet.Notice.success(
|
||||||
|
'%$1d subscribers were removed from all lists.'
|
||||||
|
.replace('%$1d', ~~response.subscribers)
|
||||||
|
.replace('%$2s', response.segment)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'confirmUnconfirmed',
|
||||||
|
label: 'Confirm unconfirmed',
|
||||||
|
onSuccess: function(response) {
|
||||||
|
MailPoet.Notice.success(
|
||||||
|
'%$1d subscribers have been confirmed.'
|
||||||
|
.replace('%$1d', ~~response.subscribers)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'trash',
|
||||||
|
label: 'Trash',
|
||||||
|
getData: function() {
|
||||||
|
return {
|
||||||
|
confirm: false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onSuccess: messages.onDelete
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
|
const SubscriberList = React.createClass({
|
||||||
|
renderItem: function(subscriber, actions) {
|
||||||
|
let row_classes = classNames(
|
||||||
|
'manage-column',
|
||||||
|
'column-primary',
|
||||||
|
'has-row-actions',
|
||||||
|
'column-username'
|
||||||
|
);
|
||||||
|
|
||||||
|
let status = '';
|
||||||
|
|
||||||
|
switch(subscriber.status) {
|
||||||
|
case 'subscribed':
|
||||||
|
status = 'Subscribed';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'unconfirmed':
|
||||||
|
status = 'Unconfirmed';
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'unsubscribed':
|
||||||
|
status = 'Unsubscribed';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
let segments = mailpoet_segments.filter(function(segment) {
|
||||||
|
return (jQuery.inArray(segment.id, subscriber.segments) !== -1);
|
||||||
|
}).map(function(segment) {
|
||||||
|
return segment.name;
|
||||||
|
}).join(', ');
|
||||||
|
|
||||||
|
let avatar = false;
|
||||||
|
if(subscriber.avatar_url) {
|
||||||
|
avatar = (
|
||||||
|
<img
|
||||||
|
className="avatar"
|
||||||
|
src={ subscriber.avatar_url }
|
||||||
|
title=""
|
||||||
|
width="32"
|
||||||
|
height="32"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<td className={ row_classes }>
|
||||||
|
<strong><Link to={ `/edit/${ subscriber.id }` }>
|
||||||
|
{ subscriber.email }
|
||||||
|
</Link></strong>
|
||||||
|
<p style={{margin: 0}}>
|
||||||
|
{ subscriber.first_name } { subscriber.last_name }
|
||||||
|
</p>
|
||||||
|
{ actions }
|
||||||
|
</td>
|
||||||
|
<td className="column" data-colname="Status">
|
||||||
|
{ status }
|
||||||
|
</td>
|
||||||
|
<td className="column" data-colname="Lists">
|
||||||
|
{ segments }
|
||||||
|
</td>
|
||||||
|
<td className="column-date" data-colname="Subscribed on">
|
||||||
|
<abbr>{ subscriber.created_at }</abbr>
|
||||||
|
</td>
|
||||||
|
<td className="column-date" data-colname="Last modified on">
|
||||||
|
<abbr>{ subscriber.updated_at }</abbr>
|
||||||
|
</td>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
render: function() {
|
||||||
|
return (
|
||||||
|
<div>
|
||||||
|
<h2 className="title">
|
||||||
|
Subscribers <Link className="add-new-h2" to="/new">New</Link>
|
||||||
|
</h2>
|
||||||
|
|
||||||
|
<Listing
|
||||||
|
location={ this.props.location }
|
||||||
|
params={ this.props.params }
|
||||||
|
endpoint="subscribers"
|
||||||
|
onRenderItem={ this.renderItem }
|
||||||
|
columns={ columns }
|
||||||
|
bulk_actions={ bulk_actions }
|
||||||
|
messages={ messages }
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
module.exports = SubscriberList;
|
@ -5,7 +5,7 @@ import SubscriberList from 'subscribers/list.jsx'
|
|||||||
import SubscriberForm from 'subscribers/form.jsx'
|
import SubscriberForm from 'subscribers/form.jsx'
|
||||||
import createHashHistory from 'history/lib/createHashHistory'
|
import createHashHistory from 'history/lib/createHashHistory'
|
||||||
|
|
||||||
let history = createHashHistory({ queryKey: false })
|
const history = createHashHistory({ queryKey: false })
|
||||||
|
|
||||||
const App = React.createClass({
|
const App = React.createClass({
|
||||||
render() {
|
render() {
|
||||||
@ -13,7 +13,7 @@ const App = React.createClass({
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
let container = document.getElementById('subscribers_container');
|
const container = document.getElementById('subscribers_container')
|
||||||
|
|
||||||
if(container) {
|
if(container) {
|
||||||
ReactDOM.render((
|
ReactDOM.render((
|
||||||
@ -22,7 +22,6 @@ if(container) {
|
|||||||
<IndexRoute component={ SubscriberList } />
|
<IndexRoute component={ SubscriberList } />
|
||||||
<Route path="new" component={ SubscriberForm } />
|
<Route path="new" component={ SubscriberForm } />
|
||||||
<Route path="edit/:id" component={ SubscriberForm } />
|
<Route path="edit/:id" component={ SubscriberForm } />
|
||||||
<Route path="filter[:filter]" component={ SubscriberList } />
|
|
||||||
<Route path="*" component={ SubscriberList } />
|
<Route path="*" component={ SubscriberList } />
|
||||||
</Route>
|
</Route>
|
||||||
</Router>
|
</Router>
|
||||||
|
@ -23,7 +23,7 @@ class Model extends \Sudzy\ValidModel {
|
|||||||
|
|
||||||
private function setTimestamp() {
|
private function setTimestamp() {
|
||||||
if($this->created_at === null) {
|
if($this->created_at === null) {
|
||||||
$this->created_at = date('Y-m-d H:i:s');
|
$this->set_expr('created_at', 'NOW()');
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user