bulk approve/reject actions for moderators

This commit is contained in:
Facu
2017-10-12 17:59:04 -03:00
parent 987acb64f5
commit e0e481373b
6 changed files with 189 additions and 120 deletions
@@ -20,3 +20,9 @@ export const toggleSelectCommentInUserDetail = (id, active) => {
};
};
export const toggleSelectAllCommentInUserDetail = (ids, active) => {
return {
type: active ? actions.SELECT_ALL_USER_DETAIL_COMMENT : actions.CLEAR_USER_DETAIL_SELECTIONS,
ids
};
};
+108 -84
View File
@@ -1,144 +1,168 @@
.copyButton {
background-color: white;
border: solid 1px;
padding: 2px 6px;
height: auto;
line-height: initial;
min-width: auto;
letter-spacing: normal;
font-size: 0.9em;
margin-left: 10px;
background-color: white;
border: solid 1px;
padding: 2px 6px;
height: auto;
line-height: initial;
min-width: auto;
letter-spacing: normal;
font-size: 0.9em;
margin-left: 10px;
}
.userDetailList {
list-style: none;
padding: 0;
margin: 0;
list-style: none;
padding: 0;
margin: 0;
}
.userDetailItem {
margin: 0 5px;
font-weight: 500;
margin: 0 5px;
font-weight: 500;
}
.stats {
display: flex;
list-style: none;
padding: 0;
margin: 0;
text-align: center;
margin: 15px 0 5px;
color: #595959;
display: flex;
list-style: none;
padding: 0;
margin: 0;
text-align: center;
margin: 15px 0 5px;
color: #595959;
}
.stat {
margin-right: 20px;
margin-right: 20px;
}
.stat:last-child {
margin-right: 0px;
margin-right: 0px;
}
.statItem, .statReportResult {
padding: 3px 5px;
background-color: #D8D8D8;
border-radius: 3px;
font-weight: 500;
display: block;
font-size: 0.9em;
line-height: normal;
letter-spacing: 0.4px;
min-width: 60px;
padding: 3px 5px;
background-color: #D8D8D8;
border-radius: 3px;
font-weight: 500;
display: block;
font-size: 0.9em;
line-height: normal;
letter-spacing: 0.4px;
min-width: 60px;
}
.statResult {
font-size: 1.5em;
padding: 5px 0;
display: inline-block;
font-size: 1.5em;
padding: 5px 0;
display: inline-block;
}
.statReportResult {
color: white;
margin: 5px 0;
font-weight: 400;
color: white;
margin: 5px 0;
font-weight: 400;
}
.statReportResult.reliable {
background-color: #749C48;
background-color: #749C48;
}
.statReportResult.neutral {
background-color: #616161;
background-color: #616161;
}
.statReportResult.unreliable {
background-color: #F44336;
background-color: #F44336;
}
.memberSince {
clear: both;
clear: both;
}
.small {
color: #888888;
font-size: 0.9em;
letter-spacing: 0.4px;
color: #888888;
font-size: 0.9em;
letter-spacing: 0.4px;
}
.profileEmail {
border: none;
background-color: transparent;
font-size: 16px;
outline: none;
width: calc(100% - 90px);
border: none;
background-color: transparent;
font-size: 16px;
outline: none;
width: calc(100% - 90px);
}
.commentStatuses {
padding: 10px 0 0 0;
margin: 0;
height: 52px;
list-style: none;
box-sizing: border-box;
li {
display: inline-block;
margin-right: 10px;
cursor: pointer;
padding: 0 10px;
}
padding: 0 0 0 10px;
margin: 0;
align-self: center;
list-style: none;
box-sizing: border-box;
li {
display: inline-block;
margin-right: 10px;
cursor: pointer;
padding: 0 10px;
}
}
.active {
font-weight: bold;
border-bottom: 3px solid #F36451;
font-weight: bold;
border-bottom: 3px solid #F36451;
}
.bulkActionGroup {
height: 52px;
background-color: #efefef;
height: 52px;
background-color: #efefef;
padding: 0 0 0 10px;
display: flex;
i {
margin-right: 0;
}
i {
margin-right: 0;
}
.bulkAction {
display: inline-block;
width: 48px;
height: 48px;
transform: scale(.7);
min-width: 0;
}
.bulkAction {
display: inline-block;
width: 48px;
height: 48px;
transform: scale(.7);
min-width: 0;
}
.bulkAction:last-child {
margin-left: -10px;
}
.bulkAction:last-child {
margin-left: -10px;
}
span{
align-self: center;
font-weight: 500;
margin-left: 15px;
}
}
.loadMore > button {
background-color: #696969;
background-color: #696969;
&:hover {
background-color: #404040;
color: white;
}
&:hover {
background-color: #404040;
color: white;
}
}
.toggleAll {
padding: 0 10px 0 0;
align-self: center;
}
.commentList {
clear: both;
}
.bulkActionHeader{
display: flex;
justify-content: space-between;
height: 52px;
&.selected{
background-color: #efefef;
}
}
+66 -35
View File
@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
import Comment from '../containers/UserDetailComment';
import styles from './UserDetail.css';
import {Icon, Button, Drawer, Spinner} from 'coral-ui';
import {Icon, Drawer, Spinner} from 'coral-ui';
import {Slot} from 'coral-framework/components';
import ButtonCopyToClipboard from './ButtonCopyToClipboard';
import ClickOutside from 'coral-framework/components/ClickOutside';
@@ -10,6 +10,8 @@ import LoadMore from '../components/LoadMore';
import cn from 'classnames';
import capitalize from 'lodash/capitalize';
import {getReliability} from 'coral-framework/utils/user';
import ApproveButton from './ApproveButton';
import RejectButton from './RejectButton';
export default class UserDetail extends React.Component {
@@ -23,6 +25,7 @@ export default class UserDetail extends React.Component {
toggleSelect: PropTypes.func.isRequired,
bulkAccept: PropTypes.func.isRequired,
bulkReject: PropTypes.func.isRequired,
toggleSelectAll: PropTypes.func.isRequired
}
rejectThenReload = async (info) => {
@@ -47,6 +50,28 @@ export default class UserDetail extends React.Component {
}
}
bulkAcceptThenReload = async () => {
try {
await this.props.bulkAccept();
this.props.data.refetch();
} catch (err) {
// TODO: handle error.
console.error(err);
}
}
bulkRejectThenReload = async () => {
try {
await this.props.bulkReject();
this.props.data.refetch();
} catch (err) {
// TODO: handle error.
console.error(err);
}
}
showAll = () => {
this.props.changeStatus('all');
}
@@ -78,11 +103,10 @@ export default class UserDetail extends React.Component {
activeTab,
selectedCommentIds,
toggleSelect,
bulkAccept,
bulkReject,
hideUserDetail,
viewUserDetail,
loadMore,
toggleSelectAll
} = this.props;
let rejectedPercent = (rejectedComments / totalComments) * 100;
@@ -100,14 +124,14 @@ export default class UserDetail extends React.Component {
<div>
<ul className={styles.userDetailList}>
<li>
<Icon name="assignment_ind"/>
<Icon name="assignment_ind" />
<span className={styles.userDetailItem}>Member Since:</span>
{new Date(user.created_at).toLocaleString()}
</li>
{user.profiles.map(({id}) =>
<li key={id}>
<Icon name="email"/>
<Icon name="email" />
<span className={styles.userDetailItem}>Email:</span>
{id} <ButtonCopyToClipboard className={styles.copyButton} icon="content_copy" copyText={id} />
</li>
@@ -142,35 +166,42 @@ export default class UserDetail extends React.Component {
queryData={{root, user}}
/>
<hr/>
{
selectedCommentIds.length === 0
? (
<ul className={styles.commentStatuses}>
<li className={activeTab === 'all' ? styles.active : ''} onClick={this.showAll}>All</li>
<li className={activeTab === 'rejected' ? styles.active : ''} onClick={this.showRejected}>Rejected</li>
</ul>
)
: (
<div className={styles.bulkActionGroup}>
<Button
onClick={bulkAccept}
className={styles.bulkAction}
cStyle='approve'
icon='done'>
</Button>
<Button
onClick={bulkReject}
className={styles.bulkAction}
cStyle='reject'
icon='close'>
</Button>
{`${selectedCommentIds.length} comments selected`}
</div>
)
}
<div>
<hr />
<div className={(selectedCommentIds.length > 0) ? cn(styles.bulkActionHeader, styles.selected) : styles.bulkActionHeader}>
{
selectedCommentIds.length === 0
? (
<ul className={styles.commentStatuses}>
<li className={activeTab === 'all' ? styles.active : ''} onClick={this.showAll}>All</li>
<li className={activeTab === 'rejected' ? styles.active : ''} onClick={this.showRejected}>Rejected</li>
</ul>
)
: (
<div className={styles.bulkActionGroup}>
<ApproveButton
onClick={this.bulkAcceptThenReload}
minimal
/>
<RejectButton
onClick={this.bulkRejectThenReload}
minimal
/>
<span> {`${selectedCommentIds.length} comments selected`}</span>
</div>
)
}
<div className={styles.toggleAll}>
<input
type='checkbox'
id='toogleAll'
checked={selectedCommentIds.length > 0 && selectedCommentIds.length === nodes.length}
onChange={(e) => {
toggleSelectAll(nodes.map((comment) => comment.id), e.target.checked);
}} />
<label htmlFor='toogleAll'>Select all</label>
</div>
</div>
<div className={styles.commentList}>
{
nodes.map((comment) => {
const selected = selectedCommentIds.indexOf(comment.id) !== -1;
@@ -199,7 +230,7 @@ export default class UserDetail extends React.Component {
);
}
render () {
render() {
if (this.props.loading) {
return this.renderLoading();
}
@@ -4,4 +4,4 @@ export const CHANGE_USER_DETAIL_STATUSES = 'CHANGE_USER_DETAIL_STATUSES';
export const SELECT_USER_DETAIL_COMMENT = 'SELECT_USER_DETAIL_COMMENT';
export const UNSELECT_USER_DETAIL_COMMENT = 'UNSELECT_USER_DETAIL_COMMENT';
export const CLEAR_USER_DETAIL_SELECTIONS = 'CLEAR_USER_DETAIL_SELECTIONS';
export const SELECT_ALL_USER_DETAIL_COMMENT = 'SELECT_ALL_USER_DETAIL_COMMENT';
@@ -11,6 +11,7 @@ import {
changeUserDetailStatuses,
clearUserDetailSelections,
toggleSelectCommentInUserDetail,
toggleSelectAllCommentInUserDetail
} from 'coral-admin/src/actions/userDetail';
import {withSetCommentStatus} from 'coral-framework/graphql/mutations';
import UserDetailComment from './UserDetailComment';
@@ -120,6 +121,7 @@ class UserDetailContainer extends React.Component {
bulkAccept={this.bulkAccept}
changeStatus={this.props.changeUserDetailStatuses}
toggleSelect={this.props.toggleSelectCommentInUserDetail}
toggleSelectAll={this.props.toggleSelectAllCommentInUserDetail}
acceptComment={this.acceptComment}
rejectComment={this.rejectComment}
loading={loading}
@@ -188,6 +190,7 @@ const mapDispatchToProps = (dispatch) => ({
toggleSelectCommentInUserDetail,
viewUserDetail,
hideUserDetail,
toggleSelectAllCommentInUserDetail
}, dispatch)
});
@@ -41,6 +41,11 @@ export default function banUserDialog(state = initialState, action) {
...state,
selectedCommentIds: state.selectedCommentIds.filter((id) => id !== action.id),
};
case actions.SELECT_ALL_USER_DETAIL_COMMENT:
return {
...state,
selectedCommentIds: action.ids
};
default:
return state;
}