Merge pull request #190 from mailpoet/batch_actions_round_1
listing: bulk actions
This commit is contained in:
@@ -45,6 +45,10 @@ function(
|
||||
|
||||
data.action = this.state.action;
|
||||
|
||||
if(action['onSuccess'] !== undefined) {
|
||||
data.onSuccess = action.onSuccess;
|
||||
}
|
||||
|
||||
if(data.action) {
|
||||
this.props.onBulkAction(selected_ids, data);
|
||||
}
|
||||
|
@@ -5,8 +5,10 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
return this.props.onSelectGroup(group);
|
||||
},
|
||||
render: function() {
|
||||
var count = this.props.groups.length;
|
||||
var groups = this.props.groups.map(function(group, index) {
|
||||
if(group.name === 'trash' && group.count === 0) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var classes = classNames(
|
||||
{ 'current' : (group.name === this.props.group) }
|
||||
@@ -14,12 +16,13 @@ define(['react', 'classnames'], function(React, classNames) {
|
||||
|
||||
return (
|
||||
<li key={index}>
|
||||
{(index > 0) ? ' |' : ''}
|
||||
<a
|
||||
href="javascript:;"
|
||||
className={classes}
|
||||
onClick={this.handleSelect.bind(this, group.name)} >
|
||||
{group.label} <span className="count">({ group.count })</span>
|
||||
</a>{(index < (count - 1)) ? ' | ' : ''}
|
||||
</a>
|
||||
</li>
|
||||
);
|
||||
}.bind(this));
|
||||
|
@@ -43,8 +43,11 @@ define(
|
||||
|
||||
return !e.target.checked;
|
||||
},
|
||||
handleDeleteItem: function(id) {
|
||||
this.props.onDeleteItem(id);
|
||||
handleRestoreItem: function(id) {
|
||||
this.props.onRestoreItem(id);
|
||||
},
|
||||
handleDeleteItem: function(id, confirm = false) {
|
||||
this.props.onDeleteItem(id, confirm);
|
||||
},
|
||||
handleToggleItem: function(id) {
|
||||
this.setState({ toggled: !this.state.toggled });
|
||||
@@ -89,26 +92,65 @@ define(
|
||||
);
|
||||
}
|
||||
|
||||
var actions = (
|
||||
<div>
|
||||
<div className="row-actions">
|
||||
{ item_actions }
|
||||
{ ' | ' }
|
||||
<span className="trash">
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={ this.handleDeleteItem.bind(null, this.props.item.id) }>
|
||||
Trash
|
||||
</a>
|
||||
</span>
|
||||
if(this.props.group === 'trash') {
|
||||
var actions = (
|
||||
<div>
|
||||
<div className="row-actions">
|
||||
<span>
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={ this.handleRestoreItem.bind(
|
||||
null,
|
||||
this.props.item.id
|
||||
)}
|
||||
>Restore</a>
|
||||
</span>
|
||||
{ ' | ' }
|
||||
<span className="delete">
|
||||
<a
|
||||
className="submitdelete"
|
||||
href="javascript:;"
|
||||
onClick={ this.handleDeleteItem.bind(
|
||||
null,
|
||||
this.props.item.id,
|
||||
true
|
||||
)}
|
||||
>Delete permanently</a>
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
||||
className="toggle-row" type="button">
|
||||
<span className="screen-reader-text">Show more details</span>
|
||||
</button>
|
||||
</div>
|
||||
<button
|
||||
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
||||
className="toggle-row" type="button">
|
||||
<span className="screen-reader-text">Show more details</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
);
|
||||
} else {
|
||||
var actions = (
|
||||
<div>
|
||||
<div className="row-actions">
|
||||
{ item_actions }
|
||||
{ ' | ' }
|
||||
<span className="trash">
|
||||
<a
|
||||
href="javascript:;"
|
||||
onClick={ this.handleDeleteItem.bind(
|
||||
null,
|
||||
this.props.item.id,
|
||||
false
|
||||
) }>
|
||||
Trash
|
||||
</a>
|
||||
</span>
|
||||
</div>
|
||||
<button
|
||||
onClick={ this.handleToggleItem.bind(null, this.props.item.id) }
|
||||
className="toggle-row" type="button">
|
||||
<span className="screen-reader-text">Show more details</span>
|
||||
</button>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
var row_classes = classNames({ 'is-expanded': !this.state.toggled })
|
||||
|
||||
@@ -190,9 +232,11 @@ define(
|
||||
onSelectItem={ this.props.onSelectItem }
|
||||
onRenderItem={ this.props.onRenderItem }
|
||||
onDeleteItem={ this.props.onDeleteItem }
|
||||
onRestoreItem={ this.props.onRestoreItem }
|
||||
selection={ this.props.selection }
|
||||
is_selectable={ this.props.is_selectable }
|
||||
item_actions={ this.props.item_actions }
|
||||
group={ this.props.group }
|
||||
key={ 'item-' + item.id }
|
||||
item={ item } />
|
||||
);
|
||||
@@ -254,7 +298,27 @@ define(
|
||||
}
|
||||
}.bind(this));
|
||||
},
|
||||
handleDeleteItem: function(id) {
|
||||
handleRestoreItem: function(id) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
page: 1
|
||||
});
|
||||
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'restore',
|
||||
data: id
|
||||
}).done(function(response) {
|
||||
if(
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onRestore'] !== undefined
|
||||
) {
|
||||
this.props.messages.onRestore(response);
|
||||
}
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
handleDeleteItem: function(id, confirm = false) {
|
||||
this.setState({
|
||||
loading: true,
|
||||
page: 1
|
||||
@@ -263,8 +327,27 @@ define(
|
||||
MailPoet.Ajax.post({
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'delete',
|
||||
data: id
|
||||
}).done(function() {
|
||||
data: {
|
||||
id: id,
|
||||
confirm: confirm
|
||||
}
|
||||
}).done(function(response) {
|
||||
if(confirm === true) {
|
||||
if(
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onConfirmDelete'] !== undefined
|
||||
) {
|
||||
this.props.messages.onConfirmDelete(response);
|
||||
}
|
||||
} else {
|
||||
if(
|
||||
this.props.messages !== undefined
|
||||
&& this.props.messages['onDelete'] !== undefined
|
||||
) {
|
||||
this.props.messages.onDelete(response);
|
||||
}
|
||||
}
|
||||
|
||||
this.getItems();
|
||||
}.bind(this));
|
||||
},
|
||||
@@ -279,6 +362,11 @@ define(
|
||||
this.setState({ loading: true });
|
||||
|
||||
var data = params || {};
|
||||
var callback = ((data['onSuccess'] !== undefined)
|
||||
? data['onSuccess']
|
||||
: function() {}
|
||||
);
|
||||
delete data.onSuccess;
|
||||
|
||||
data.listing = {
|
||||
offset: 0,
|
||||
@@ -293,8 +381,9 @@ define(
|
||||
endpoint: this.props.endpoint,
|
||||
action: 'bulk_action',
|
||||
data: data
|
||||
}).done(function() {
|
||||
}).done(function(response) {
|
||||
this.getItems();
|
||||
callback(response);
|
||||
}.bind(this));
|
||||
},
|
||||
handleSearch: function(search) {
|
||||
@@ -414,6 +503,24 @@ define(
|
||||
// bulk actions
|
||||
var bulk_actions = this.props.bulk_actions || [];
|
||||
|
||||
if(this.state.group === 'trash') {
|
||||
bulk_actions = [
|
||||
{
|
||||
name: 'restore',
|
||||
label: 'Restore',
|
||||
onSuccess: this.props.messages.onRestore
|
||||
},
|
||||
{
|
||||
name: 'trash',
|
||||
label: 'Delete permanently',
|
||||
onSuccess: this.props.messages.onConfirmDelete,
|
||||
getData: function() {
|
||||
return { confirm: true };
|
||||
}
|
||||
}
|
||||
];
|
||||
}
|
||||
|
||||
// item actions
|
||||
var item_actions = this.props.item_actions || [];
|
||||
|
||||
@@ -464,6 +571,7 @@ define(
|
||||
<ListingItems
|
||||
onRenderItem={ this.handleRenderItem }
|
||||
onDeleteItem={ this.handleDeleteItem }
|
||||
onRestoreItem={ this.handleRestoreItem }
|
||||
columns={ this.props.columns }
|
||||
is_selectable={ bulk_actions.length > 0 }
|
||||
onSelectItem={ this.handleSelectItem }
|
||||
@@ -471,6 +579,7 @@ define(
|
||||
selection={ this.state.selection }
|
||||
selected_ids={ this.state.selected_ids }
|
||||
loading={ this.state.loading }
|
||||
group={ this.state.group }
|
||||
count={ this.state.count }
|
||||
limit={ this.state.limit }
|
||||
item_actions={ item_actions }
|
||||
|
@@ -121,6 +121,22 @@ define('notice', ['mailpoet', 'jquery'], function(MailPoet, jQuery) {
|
||||
// set message
|
||||
this.setMessage(this.options.message);
|
||||
|
||||
// position notice
|
||||
this.element.insertAfter(jQuery('h2.title'));
|
||||
|
||||
// set class name
|
||||
switch(this.options.type) {
|
||||
case 'success':
|
||||
this.element.addClass('updated');
|
||||
break;
|
||||
case 'system':
|
||||
this.element.addClass('update-nag');
|
||||
break;
|
||||
case 'error':
|
||||
this.element.addClass('error');
|
||||
break;
|
||||
}
|
||||
|
||||
// make the notice appear
|
||||
this.element.fadeIn(200);
|
||||
|
||||
|
@@ -59,6 +59,63 @@ define(
|
||||
},
|
||||
];
|
||||
|
||||
var messages = {
|
||||
onDelete: function(response) {
|
||||
var count = ~~response.subscribers;
|
||||
var message = null;
|
||||
|
||||
if(count === 1) {
|
||||
message = (
|
||||
'1 subscriber was moved to the trash.'
|
||||
).replace('%$1d', count);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d subscribers were moved to the trash.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
},
|
||||
onConfirmDelete: function(response) {
|
||||
var count = ~~response.subscribers;
|
||||
var message = null;
|
||||
|
||||
if(count === 1) {
|
||||
message = (
|
||||
'1 subscriber was permanently deleted.'
|
||||
).replace('%$1d', count);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d subscribers were permanently deleted.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
},
|
||||
onRestore: function(response) {
|
||||
var count = ~~response.subscribers;
|
||||
var message = null;
|
||||
|
||||
if(count === 1) {
|
||||
message = (
|
||||
'1 subscriber has been restored from the trash.'
|
||||
).replace('%$1d', count);
|
||||
} else if(count > 1) {
|
||||
message = (
|
||||
'%$1d subscribers have been restored from the trash.'
|
||||
).replace('%$1d', count);
|
||||
}
|
||||
|
||||
if(message !== null) {
|
||||
MailPoet.Notice.success(message);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var bulk_actions = [
|
||||
{
|
||||
name: 'moveToList',
|
||||
@@ -77,6 +134,13 @@ define(
|
||||
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)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -96,6 +160,13 @@ define(
|
||||
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)
|
||||
);
|
||||
}
|
||||
},
|
||||
{
|
||||
@@ -115,11 +186,45 @@ define(
|
||||
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'
|
||||
label: 'Trash',
|
||||
getData: function() {
|
||||
return {
|
||||
confirm: false
|
||||
}
|
||||
},
|
||||
onSuccess: messages.onDelete
|
||||
}
|
||||
];
|
||||
|
||||
@@ -153,14 +258,14 @@ define(
|
||||
return segment.name;
|
||||
}).join(', ');
|
||||
|
||||
var row_actions = false;
|
||||
|
||||
return (
|
||||
<div>
|
||||
<td className={ row_classes }>
|
||||
<strong>
|
||||
<Link to={ `/edit/${ subscriber.id }` }>
|
||||
{ subscriber.email }
|
||||
</Link>
|
||||
</strong>
|
||||
<strong><Link to={ `/edit/${ subscriber.id }` }>
|
||||
{ subscriber.email }
|
||||
</Link></strong>
|
||||
{ actions }
|
||||
</td>
|
||||
<td className="column" data-colname="First name">
|
||||
@@ -195,7 +300,9 @@ define(
|
||||
endpoint="subscribers"
|
||||
onRenderItem={ this.renderItem }
|
||||
columns={ columns }
|
||||
bulk_actions={ bulk_actions } />
|
||||
bulk_actions={ bulk_actions }
|
||||
messages={ messages }
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
Reference in New Issue
Block a user