diff --git a/client/coral-admin/src/routes/Community/components/People.css b/client/coral-admin/src/routes/Community/components/People.css
index ac565c8f8..aea321cd7 100644
--- a/client/coral-admin/src/routes/Community/components/People.css
+++ b/client/coral-admin/src/routes/Community/components/People.css
@@ -34,6 +34,21 @@
}
}
+.filterHeader {
+ margin-top: 30px;
+ font-size: 16px;
+ font-weight: 900;
+}
+
+.filterDetail {
+ margin-top: 15px;
+ font-size: 16px;
+}
+
+.radioGroup {
+ margin: 5px 0;
+}
+
.mainContent {
width: calc(100% - 300px);
padding: 34px 14px;
diff --git a/client/coral-admin/src/routes/Community/components/People.js b/client/coral-admin/src/routes/Community/components/People.js
index e0d699304..e40b6cca4 100644
--- a/client/coral-admin/src/routes/Community/components/People.js
+++ b/client/coral-admin/src/routes/Community/components/People.js
@@ -9,6 +9,7 @@ import PropTypes from 'prop-types';
import ActionsMenu from 'coral-admin/src/components/ActionsMenu';
import ActionsMenuItem from 'coral-admin/src/components/ActionsMenuItem';
import { isSuspended, isBanned } from 'coral-framework/utils/user';
+import { RadioGroup, Radio } from 'react-mdl';
import moment from 'moment';
import {
ADMIN,
@@ -64,11 +65,12 @@ class People extends React.Component {
render() {
const {
- onSearchChange,
+ onFilterChange,
users = [],
setUserRole,
viewUserDetail,
loadMore,
+ filters,
} = this.props;
const hasResults = !!users.nodes.length;
@@ -87,11 +89,43 @@ class People extends React.Component {
id="commenters-search"
type="text"
className={styles.searchBoxInput}
- defaultValue=""
- onChange={onSearchChange}
+ value={filters.search}
+ onChange={onFilterChange('search')}
placeholder={t('streams.search')}
/>
+
{hasResults ? (
@@ -254,7 +288,8 @@ class People extends React.Component {
People.propTypes = {
users: PropTypes.object.isRequired,
- onSearchChange: PropTypes.func.isRequired,
+ filters: PropTypes.object.isRequired,
+ onFilterChange: PropTypes.func.isRequired,
setUserRole: PropTypes.func.isRequired,
viewUserDetail: PropTypes.func.isRequired,
unbanUser: PropTypes.func.isRequired,
diff --git a/client/coral-admin/src/routes/Community/containers/People.js b/client/coral-admin/src/routes/Community/containers/People.js
index 63cd28833..7f47795b4 100644
--- a/client/coral-admin/src/routes/Community/containers/People.js
+++ b/client/coral-admin/src/routes/Community/containers/People.js
@@ -17,59 +17,65 @@ import update from 'immutability-helper';
import { Spinner } from 'coral-ui';
import withQuery from 'coral-framework/hocs/withQuery';
-class PeopleContainer extends React.Component {
+class PeopleContainer extends React.PureComponent {
timer = null;
state = {
- searchValue: '',
+ search: '',
+ role: '',
+ status: '',
};
- onSearchChange = e => {
- const { value } = e.target;
- this.setState({ searchValue: value }, () => {
+ statusToQuery = {
+ active: { suspended: false, banned: false },
+ suspended: { suspended: true },
+ banned: { banned: true },
+ };
+
+ onFilterChange = filter => e =>
+ this.setState({ [filter]: e.target.value }, () => {
clearTimeout(this.timer);
- this.timer = setTimeout(() => {
- this.search(value);
- }, 350);
+ this.timer = setTimeout(this.filter, 350);
});
+
+ getFilterState = () => {
+ const { role, status } = this.state;
+
+ return {
+ status: this.statusToQuery[status] || null,
+ role: role || null,
+ };
};
- search = async value => {
- return this.props.data.fetchMore({
- query: SEARCH_QUERY,
+ filter = () =>
+ this.props.data.fetchMore({
+ query: FILTER_QUERY,
variables: {
- value,
+ state: this.getFilterState(),
+ value: this.state.search,
limit: 10,
},
- updateQuery: (previous, { fetchMoreResult: { users } }) => {
- const updated = update(previous, {
+ updateQuery: (previous, { fetchMoreResult: { users } }) =>
+ update(previous, {
users: {
- nodes: {
- $set: users.nodes,
- },
+ nodes: { $set: users.nodes },
hasNextPage: { $set: users.hasNextPage },
endCursor: { $set: users.endCursor },
},
- });
- return updated;
- },
+ }),
});
- };
- setUserRole = async (id, role) => {
- await this.props.setUserRole(id, role);
- };
-
- loadMore = () => {
- return this.props.data.fetchMore({
+ loadMore = () =>
+ this.props.data.fetchMore({
query: LOAD_MORE_QUERY,
variables: {
- value: this.state.searchValue,
- limit: 5,
cursor: this.props.root.users.endCursor,
+ state: this.getFilterState(),
+ value: this.state.search,
+ limit: 5,
},
- updateQuery: (previous, { fetchMoreResult: { users } }) => {
- const updated = update(previous, {
+ updateQuery: (previous, { fetchMoreResult: { users } }) =>
+ update(previous, {
users: {
nodes: {
$apply: nodes => appendNewNodes(nodes, users.nodes),
@@ -77,11 +83,8 @@ class PeopleContainer extends React.Component {
hasNextPage: { $set: users.hasNextPage },
endCursor: { $set: users.endCursor },
},
- });
- return updated;
- },
+ }),
});
- };
render() {
if (this.props.data.error) {
@@ -98,9 +101,9 @@ class PeopleContainer extends React.Component {
return (
);
}
@@ -125,16 +129,6 @@ PeopleContainer.propTypes = {
root: PropTypes.object,
};
-const mapDispatchToProps = dispatch =>
- bindActionCreators(
- {
- viewUserDetail,
- showSuspendUserDialog,
- showBanUserDialog,
- },
- dispatch
- );
-
const LOAD_MORE_QUERY = gql`
query TalkAdmin_Community_People_LoadMoreUsers(
$limit: Int
@@ -169,9 +163,13 @@ const LOAD_MORE_QUERY = gql`
}
`;
-const SEARCH_QUERY = gql`
- query TalkAdmin_Community_People_SearchUsers($value: String, $limit: Int) {
- users(query: { value: $value, limit: $limit }) {
+const FILTER_QUERY = gql`
+ query TalkAdmin_Community_People_FilterUsers(
+ $state: UserStateInput
+ $value: String
+ $limit: Int
+ ) {
+ users(query: { state: $state, value: $value, limit: $limit }) {
hasNextPage
endCursor
nodes {
@@ -199,6 +197,16 @@ const SEARCH_QUERY = gql`
}
`;
+const mapDispatchToProps = dispatch =>
+ bindActionCreators(
+ {
+ viewUserDetail,
+ showSuspendUserDialog,
+ showBanUserDialog,
+ },
+ dispatch
+ );
+
export default compose(
connect(
null,
diff --git a/graph/loaders/users.js b/graph/loaders/users.js
index d8e1927cb..11d35ac95 100644
--- a/graph/loaders/users.js
+++ b/graph/loaders/users.js
@@ -4,7 +4,11 @@ const { SEARCH_OTHER_USERS } = require('../../perms/constants');
const { escapeRegExp } = require('../../services/regex');
const mergeState = (query, state) => {
- const { status } = state;
+ const { role, status } = state;
+
+ if (role) {
+ query.merge({ role });
+ }
if (status) {
const { username, banned, suspended } = status;
diff --git a/graph/typeDefs.graphql b/graph/typeDefs.graphql
index 1ca003de6..ebbf8203d 100644
--- a/graph/typeDefs.graphql
+++ b/graph/typeDefs.graphql
@@ -189,6 +189,7 @@ type UserStatus {
}
input UserStateInput {
+ role: USER_ROLES
status: UserStatusInput
}
diff --git a/locales/en.yml b/locales/en.yml
index c7b4ed926..e8b276fb2 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -62,6 +62,7 @@ en:
active: Active
admin: Administrator
ads_marketing: 'This looks like an ad/marketing'
+ all: 'All'
are_you_sure: 'Are you sure you would like to ban {0}?'
ban_user: 'Ban User?'
banned: Banned
@@ -69,6 +70,8 @@ en:
cancel: Cancel
commenter: Commenter
dont_like_username: 'Dislike username'
+ filter_users: 'Filter users'
+ filter_role: Role
flaggedaccounts: 'Reported Usernames'
flags: Flags
impersonating: Impersonation
@@ -85,6 +88,7 @@ en:
spam_ads: Spam/Ads
staff: Staff
status: Status
+ suspended: Suspended
username_and_email: 'Username and Email'
yes_ban_user: 'Yes Ban User'
configure: