From 67cca7d85d8d4f1baa8e75a0b1fa591d3e072b97 Mon Sep 17 00:00:00 2001 From: hovoodd Date: Wed, 3 Oct 2018 22:15:05 +0300 Subject: [PATCH 1/3] Add filters to list of people --- .../routes/Community/components/People.css | 15 +++ .../src/routes/Community/components/People.js | 43 ++++++- .../src/routes/Community/containers/People.js | 108 ++++++++++-------- graph/loaders/users.js | 6 +- graph/typeDefs.graphql | 1 + locales/en.yml | 4 + 6 files changed, 122 insertions(+), 55 deletions(-) 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')} /> +
+ {t('community.filter_users')} +
+
{t('community.status')}
+ + {t('community.all')} + {t('community.active')} + {t('community.suspended')} + {t('community.banned')} + +
+ {t('community.filter_role')} +
+ + {t('community.all')} + {t('community.admin')} + {t('community.staff')} + {t('community.moderator')} + {t('community.commenter')} +
{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: From 8b2dc3a46de310287c8046ae7f2c4fbc96e42bfa Mon Sep 17 00:00:00 2001 From: Bryce Kahle Date: Mon, 8 Oct 2018 13:02:37 -0400 Subject: [PATCH 2/3] do not show admin drawer unless user has access Fixes https://github.com/coralproject/talk/issues/1791 --- client/coral-admin/src/components/Drawer.js | 11 +++++------ test/e2e/page_objects/admin.js | 4 ++++ test/e2e/specs/02_admin.js | 7 ++++++- 3 files changed, 15 insertions(+), 7 deletions(-) diff --git a/client/coral-admin/src/components/Drawer.js b/client/coral-admin/src/components/Drawer.js index 25f21a455..2c463b28e 100644 --- a/client/coral-admin/src/components/Drawer.js +++ b/client/coral-admin/src/components/Drawer.js @@ -7,9 +7,9 @@ import t from 'coral-framework/services/i18n'; import { can } from 'coral-framework/services/perms'; import cn from 'classnames'; -const CoralDrawer = ({ handleLogout, currentUser }) => ( - - {currentUser && can(currentUser, 'ACCESS_ADMIN') ? ( +const CoralDrawer = ({ handleLogout, currentUser }) => + currentUser && can(currentUser, 'ACCESS_ADMIN') ? ( +
{can(currentUser, 'MODERATE_COMMENTS') && ( @@ -48,9 +48,8 @@ const CoralDrawer = ({ handleLogout, currentUser }) => ( {`v${process.env.VERSION}`}
- ) : null} -
-); +
+ ) : null; CoralDrawer.propTypes = { handleLogout: PropTypes.func.isRequired, diff --git a/test/e2e/page_objects/admin.js b/test/e2e/page_objects/admin.js index d436eddc7..4d540a7b4 100644 --- a/test/e2e/page_objects/admin.js +++ b/test/e2e/page_objects/admin.js @@ -30,6 +30,10 @@ module.exports = { .waitForElementVisible('@signOutButton') .click('@signOutButton'); }, + login(user) { + this.expect.section('@login').to.be.visible; + return this.section.login.login(user); + }, navigateAndLogin(user) { this.navigate().expect.section('@login').to.be.visible; return this.section.login.login(user); diff --git a/test/e2e/specs/02_admin.js b/test/e2e/specs/02_admin.js index 5acfd83f6..4fab8b157 100644 --- a/test/e2e/specs/02_admin.js +++ b/test/e2e/specs/02_admin.js @@ -20,13 +20,18 @@ module.exports = { client.end(); }, + 'Admin goes to login': client => { + const adminPage = client.page.admin(); + adminPage.navigate().expect.element('drawerButton').to.not.be.present; + }, + 'Admin logs in': client => { const adminPage = client.page.admin(); const { testData: { admin }, } = client.globals; - adminPage.navigateAndLogin(admin); + adminPage.login(admin); }, 'Admin goes to Stories': client => { From aecb7de019989ce6d8a488b575832af3c50a6a08 Mon Sep 17 00:00:00 2001 From: Kim Gardner Date: Fri, 12 Oct 2018 18:24:55 +0100 Subject: [PATCH 3/3] Add Spanish translations --- locales/es.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/locales/es.yml b/locales/es.yml index 6cc5af65b..be01e0fce 100644 --- a/locales/es.yml +++ b/locales/es.yml @@ -57,6 +57,7 @@ es: active: Activa admin: Administrator ads_marketing: 'Esto parece ser un ad/marketing' + all: 'Todos' are_you_sure: '¿Estás segura que quieres suspender a {0}?' ban_user: '¿Quieres suspender al Usuario?' banned: Suspendido @@ -64,6 +65,8 @@ es: cancel: Cancelar commenter: Comentarista dont_like_username: 'No me gusta este nombre de usuario' + filter_users: 'Filter users' + filter_role: Rol flaggedaccounts: 'Nombres de Usuario Reportados' flags: Reportes impersonating: Impersonando @@ -80,6 +83,7 @@ es: spam_ads: Spam/Publicidad staff: Personal status: Estado + suspended: Suspendido username_and_email: 'Usuario y Correo' yes_ban_user: 'Si, Suspendan el usuario' configure: