Manage subscriptions

- make use of the SubscriberSegment::status column to keep track of unsubscriptions
- unsubscribed segments now appear grayed out in the Subscribers listing
- added unsubscribed_at after segment names when editing a subscriber
- added date() method for Twig (uses WP's date format / date_offset)
- fixed typo in Form iframe export
- fixed unit test for Newsletters
- updated selection component (JSX) to allow more customization
This commit is contained in:
Jonathan Labreuille
2016-02-22 11:35:34 +01:00
parent acf300160d
commit 07d533a810
19 changed files with 313 additions and 138 deletions

View File

@@ -26,7 +26,7 @@ function(
&& (this.props.item.id !== prevProps.item.id)
) {
jQuery('#'+this.refs.select.id)
.val(this.props.item[this.props.field.name])
.val(this.getSelectedValues())
.trigger('change');
}
},
@@ -45,7 +45,11 @@ function(
if(item.element && item.element.selected) {
return null;
} else {
return item.text;
if(item.title) {
return item.title;
} else {
return item.text;
}
}
}
});
@@ -65,15 +69,25 @@ function(
select2.select2(
'val',
this.props.item[this.props.field.name]
this.getSelectedValues()
);
this.setState({ initialized: true });
},
getSelectedValues: function() {
if(this.props.field['selected'] !== undefined) {
return this.props.field['selected'](this.props.item);
} else if(this.props.item !== undefined && this.props.field.name !== undefined) {
return this.props.item[this.props.field.name];
} else {
return null;
}
},
loadCachedItems: function() {
if(typeof(window['mailpoet_'+this.props.field.endpoint]) !== 'undefined') {
var items = window['mailpoet_'+this.props.field.endpoint];
if(this.props.field['filter'] !== undefined) {
items = items.filter(this.props.field.filter);
}
@@ -98,31 +112,48 @@ function(
});
}
},
getLabel: function(item) {
if(this.props.field['getLabel'] !== undefined) {
return this.props.field.getLabel(item, this.props.item);
}
return item.name;
},
getSearchLabel: function(item) {
if(this.props.field['getSearchLabel'] !== undefined) {
return this.props.field.getSearchLabel(item, this.props.item);
}
return null;
},
getValue: function(item) {
if(this.props.field['getValue'] !== undefined) {
return this.props.field.getValue(item, this.props.item);
}
return item.id;
},
render: function() {
var options = this.state.items.map(function(item, index) {
const options = this.state.items.map((item, index) => {
let label = this.getLabel(item);
let searchLabel = this.getSearchLabel(item);
let value = this.getValue(item);
return (
<option
key={ item.id }
value={ item.id }
key={ 'option-'+index }
value={ value }
title={ searchLabel }
>
{ item.name }
{ label }
</option>
);
});
var default_value = (
(this.props.item !== undefined && this.props.field.name !== undefined)
? this.props.item[this.props.field.name]
: null
);
return (
<select
id={ this.props.field.id || this.props.field.name }
ref="select"
data-placeholder={ this.props.field.placeholder }
multiple={ this.props.field.multiple }
defaultValue={ default_value }
defaultValue={ this.getSelectedValues() }
{...this.props.field.validation}
>{ options }</select>
);

View File

@@ -181,13 +181,13 @@ const SegmentList = React.createClass({
<abbr>{ segment.description }</abbr>
</td>
<td className="column-date" data-colname="Subscribed">
<abbr>{ segment.subscribed || 0 }</abbr>
<abbr>{ segment.subscribers_count.subscribed || 0 }</abbr>
</td>
<td className="column-date" data-colname="Unconfirmed">
<abbr>{ segment.unconfirmed || 0 }</abbr>
<abbr>{ segment.subscribers_count.unconfirmed || 0 }</abbr>
</td>
<td className="column-date" data-colname="Unsubscribed">
<abbr>{ segment.unsubscribed || 0 }</abbr>
<abbr>{ segment.subscribers_count.unsubscribed || 0 }</abbr>
</td>
<td className="column-date" data-colname="Created on">
<abbr>{ segment.created_at }</abbr>

View File

@@ -3,15 +3,16 @@ define(
'react',
'react-router',
'mailpoet',
'form/form.jsx'
'form/form.jsx',
'moment'
],
function(
React,
Router,
MailPoet,
Form
Form,
Moment
) {
var fields = [
{
name: 'email',
@@ -45,8 +46,38 @@ define(
placeholder: "Select a list",
endpoint: "segments",
multiple: true,
selected: function(subscriber) {
if (Array.isArray(subscriber.subscriptions) === false) {
return null;
}
return subscriber.subscriptions.map(function(subscription) {
if (subscription.status === 'subscribed') {
return subscription.segment_id;
}
});
},
filter: function(segment) {
return !!(!segment.deleted_at);
},
getSearchLabel: function(segment, subscriber) {
let label = '';
if (subscriber.subscriptions !== undefined) {
subscriber.subscriptions.map(function(subscription) {
if (segment.id === subscription.segment_id) {
label = segment.name;
if (subscription.status === 'unsubscribed') {
const unsubscribed_at = Moment(subscription.updated_at)
.utcOffset(parseInt(mailpoet_date_offset))
.format('ddd, D MMM YYYY HH:mm:ss');
label += ' (Unsubscribed on '+unsubscribed_at+')';
}
}
});
}
return label;
}
}
];
@@ -58,11 +89,11 @@ define(
label: custom_field.name,
type: custom_field.type
};
if(custom_field.params) {
if (custom_field.params) {
field.params = custom_field.params;
}
if(custom_field.params.values) {
if (custom_field.params.values) {
field.values = custom_field.params.values;
}

View File

@@ -231,6 +231,15 @@ const item_actions = [
];
const SubscriberList = React.createClass({
getSegmentFromId: function(segment_id) {
let result = false;
mailpoet_segments.map(function(segment) {
if (segment.id === segment_id) {
result = segment;
}
});
return result;
},
renderItem: function(subscriber, actions) {
let row_classes = classNames(
'manage-column',
@@ -255,11 +264,41 @@ const SubscriberList = React.createClass({
break;
}
let segments = mailpoet_segments.filter(function(segment) {
return (jQuery.inArray(segment.id, subscriber.segments) !== -1);
}).map(function(segment) {
return segment.name;
}).join(', ');
let segments = false;
if (subscriber.subscriptions.length > 0) {
let subscribed_segments = [];
let unsubscribed_segments = [];
subscriber.subscriptions.map((subscription) => {
const segment = this.getSegmentFromId(subscription.segment_id);
if (subscription.status === 'subscribed') {
subscribed_segments.push(segment.name);
} else {
unsubscribed_segments.push(segment.name);
}
});
segments = (
<span>
<span className="mailpoet_segments_subscribed">
{ subscribed_segments.join(', ') }
{
(
subscribed_segments.length > 0
&& unsubscribed_segments.length > 0
) ? ' / ' : ''
}
</span>
<span
className="mailpoet_segments_unsubscribed"
title="Lists to which the subscriber was subscribed."
>
{ unsubscribed_segments.join(', ') }
</span>
</span>
);
}
let avatar = false;
if(subscriber.avatar_url) {