diff --git a/client/coral-admin/src/actions/community.js b/client/coral-admin/src/actions/community.js index 921f0a0a2..6a091d214 100644 --- a/client/coral-admin/src/actions/community.js +++ b/client/coral-admin/src/actions/community.js @@ -1,11 +1,12 @@ import queryString from 'query-string'; import { - FETCH_COMMENTERS_REQUEST, - FETCH_COMMENTERS_SUCCESS, - FETCH_COMMENTERS_FAILURE, + FETCH_USERS_REQUEST, + FETCH_USERS_SUCCESS, + FETCH_USERS_FAILURE, SORT_UPDATE, - COMMENTERS_NEW_PAGE, + SET_PAGE, + SET_SEARCH_VALUE, SET_ROLE, SET_COMMENTER_STATUS, SHOW_BANUSER_DIALOG, @@ -16,13 +17,13 @@ import { import t from 'coral-framework/services/i18n'; -export const fetchAccounts = (query = {}) => (dispatch, _, {rest}) => { - dispatch(requestFetchAccounts()); +export const fetchUsers = (query = {}) => (dispatch, _, {rest}) => { + dispatch(requestFetchUsers()); rest(`/users?${queryString.stringify(query)}`) .then(({result, page, count, limit, totalPages}) =>{ dispatch({ - type: FETCH_COMMENTERS_SUCCESS, - accounts: result, + type: FETCH_USERS_SUCCESS, + users: result, page, count, limit, @@ -32,12 +33,12 @@ export const fetchAccounts = (query = {}) => (dispatch, _, {rest}) => { .catch((error) => { console.error(error); const errorMessage = error.translation_key ? t(`error.${error.translation_key}`) : error.toString(); - dispatch({type: FETCH_COMMENTERS_FAILURE, error: errorMessage}); + dispatch({type: FETCH_USERS_FAILURE, error: errorMessage}); }); }; -const requestFetchAccounts = () => ({ - type: FETCH_COMMENTERS_REQUEST +const requestFetchUsers = () => ({ + type: FETCH_USERS_REQUEST }); export const updateSorting = (sort) => ({ @@ -45,8 +46,14 @@ export const updateSorting = (sort) => ({ sort }); -export const newPage = () => ({ - type: COMMENTERS_NEW_PAGE +export const setPage = (page) => ({ + type: SET_PAGE, + page, +}); + +export const setSearchValue = (value) => ({ + type: SET_SEARCH_VALUE, + value, }); export const setRole = (id, role) => (dispatch, _, {rest}) => { diff --git a/client/coral-admin/src/actions/assets.js b/client/coral-admin/src/actions/stories.js similarity index 70% rename from client/coral-admin/src/actions/assets.js rename to client/coral-admin/src/actions/stories.js index 1ce6ad6ac..2524b0c3c 100644 --- a/client/coral-admin/src/actions/assets.js +++ b/client/coral-admin/src/actions/stories.js @@ -1,12 +1,17 @@ +import queryString from 'query-string'; + import { FETCH_ASSETS_REQUEST, FETCH_ASSETS_SUCCESS, FETCH_ASSETS_FAILURE, + SET_PAGE, + SET_SEARCH_VALUE, + SET_CRITERIA, UPDATE_ASSET_STATE_REQUEST, UPDATE_ASSET_STATE_SUCCESS, UPDATE_ASSET_STATE_FAILURE, UPDATE_ASSETS -} from '../constants/assets'; +} from '../constants/stories'; import t from 'coral-framework/services/i18n'; @@ -16,13 +21,16 @@ import t from 'coral-framework/services/i18n'; // Fetch a page of assets // Get comments to fill each of the three lists on the mod queue -export const fetchAssets = (skip = '', limit = '', search = '', sort = '', filter = '') => (dispatch, _, {rest}) => { +export const fetchAssets = (query = {}) => (dispatch, _, {rest}) => { dispatch({type: FETCH_ASSETS_REQUEST}); - return rest(`/assets?skip=${skip}&limit=${limit}&sort=${sort}&search=${search}&filter=${filter}`) - .then(({result, count}) => + return rest(`/assets?${queryString.stringify(query)}`) + .then(({result, page, count, limit, totalPages}) => dispatch({type: FETCH_ASSETS_SUCCESS, assets: result, - count + page, + count, + limit, + totalPages, })) .catch((error) => { console.error(error); @@ -47,3 +55,19 @@ export const updateAssetState = (id, closedAt) => (dispatch, _, {rest}) => { export const updateAssets = (assets) => (dispatch) => { dispatch({type: UPDATE_ASSETS, assets}); }; + +export const setPage = (page) => ({ + type: SET_PAGE, + page, +}); + +export const setSearchValue = (value) => ({ + type: SET_SEARCH_VALUE, + value, +}); + +export const setCriteria = (criteria) => ({ + type: SET_CRITERIA, + criteria, +}); + diff --git a/client/coral-admin/src/components/ui/Header.js b/client/coral-admin/src/components/ui/Header.js index 62f6a7fd1..c5af63786 100644 --- a/client/coral-admin/src/components/ui/Header.js +++ b/client/coral-admin/src/components/ui/Header.js @@ -1,4 +1,5 @@ import React from 'react'; +import cn from 'classnames'; import PropTypes from 'prop-types'; import {Navigation, Header, IconButton, MenuItem, Menu} from 'react-mdl'; import {Link, IndexLink} from 'react-router'; @@ -24,7 +25,7 @@ const CoralHeader = ({ {t('configure.dashboard')} @@ -33,7 +34,7 @@ const CoralHeader = ({ can(auth.user, 'MODERATE_COMMENTS') && ( {t('configure.moderate')} @@ -42,8 +43,8 @@ const CoralHeader = ({ ) } {t('configure.stories')} @@ -51,7 +52,7 @@ const CoralHeader = ({ {t('configure.community')} @@ -62,7 +63,7 @@ const CoralHeader = ({ can(auth.user, 'UPDATE_CONFIG') && ( {t('configure.configure')} diff --git a/client/coral-admin/src/constants/assets.js b/client/coral-admin/src/constants/assets.js deleted file mode 100644 index 20ec0a9c3..000000000 --- a/client/coral-admin/src/constants/assets.js +++ /dev/null @@ -1,9 +0,0 @@ -export const FETCH_ASSETS_REQUEST = 'FETCH_ASSETS_REQUEST'; -export const FETCH_ASSETS_SUCCESS = 'FETCH_ASSETS_SUCCESS'; -export const FETCH_ASSETS_FAILURE = 'FETCH_ASSETS_FAILURE'; - -export const UPDATE_ASSET_STATE_REQUEST = 'UPDATE_ASSET_STATE_REQUEST'; -export const UPDATE_ASSET_STATE_SUCCESS = 'UPDATE_ASSET_STATE_SUCCESS'; -export const UPDATE_ASSET_STATE_FAILURE = 'UPDATE_ASSET_STATE_FAILURE'; - -export const UPDATE_ASSETS = 'UPDATE_ASSETS'; diff --git a/client/coral-admin/src/constants/community.js b/client/coral-admin/src/constants/community.js index be11e9b0c..eea946efb 100644 --- a/client/coral-admin/src/constants/community.js +++ b/client/coral-admin/src/constants/community.js @@ -1,17 +1,22 @@ -export const FETCH_COMMENTERS_REQUEST = 'FETCH_COMMENTERS_REQUEST'; -export const FETCH_COMMENTERS_SUCCESS = 'FETCH_COMMENTERS_SUCCESS'; -export const FETCH_COMMENTERS_FAILURE = 'FETCH_COMMENTERS_FAILURE'; -export const SORT_UPDATE = 'SORT_UPDATE'; -export const COMMENTERS_NEW_PAGE = 'COMMENTERS_NEW_PAGE'; -export const SET_ROLE = 'SET_ROLE'; -export const SET_COMMENTER_STATUS = 'SET_COMMENTER_STATUS'; +const prefix = 'COMMUNITY'; -export const FETCH_FLAGGED_COMMENTERS_REQUEST = 'FETCH_FLAGGED_COMMENTERS_REQUEST'; -export const FETCH_FLAGGED_COMMENTERS_SUCCESS = 'FETCH_FLAGGED_COMMENTERS_SUCCESS'; -export const FETCH_FLAGGED_COMMENTERS_FAILURE = 'FETCH_FLAGGED_COMMENTERS_FAILURE'; +export const FETCH_USERS_REQUEST = `${prefix}_FETCH_USERS_REQUEST`; +export const FETCH_USERS_SUCCESS = `${prefix}_FETCH_USERS_SUCCESS`; +export const FETCH_USERS_FAILURE = `${prefix}_FETCH_USERS_FAILURE`; -export const SHOW_BANUSER_DIALOG = 'SHOW_BANUSER_DIALOG'; -export const HIDE_BANUSER_DIALOG = 'HIDE_BANUSER_DIALOG'; +export const SORT_UPDATE = `${prefix}_SORT_UPDATE`; +export const SET_PAGE = `${prefix}_SET_PAGE`; +export const SET_ROLE = `${prefix}_SET_ROLE`; +export const SET_COMMENTER_STATUS = `${prefix}_SET_COMMENTER_STATUS`; -export const SHOW_REJECT_USERNAME_DIALOG = 'SHOW_REJECT_USERNAME_DIALOG'; -export const HIDE_REJECT_USERNAME_DIALOG = 'HIDE_REJECT_USERNAME_DIALOG'; +export const FETCH_FLAGGED_COMMENTERS_REQUEST = `${prefix}_FETCH_FLAGGED_COMMENTERS_REQUEST`; +export const FETCH_FLAGGED_COMMENTERS_SUCCESS = `${prefix}_FETCH_FLAGGED_COMMENTERS_SUCCESS`; +export const FETCH_FLAGGED_COMMENTERS_FAILURE = `${prefix}_FETCH_FLAGGED_COMMENTERS_FAILURE`; + +export const SHOW_BANUSER_DIALOG = `${prefix}_SHOW_BANUSER_DIALOG`; +export const HIDE_BANUSER_DIALOG = `${prefix}_HIDE_BANUSER_DIALOG`; + +export const SHOW_REJECT_USERNAME_DIALOG = `${prefix}_SHOW_REJECT_USERNAME_DIALOG`; +export const HIDE_REJECT_USERNAME_DIALOG = `${prefix}_HIDE_REJECT_USERNAME_DIALOG`; + +export const SET_SEARCH_VALUE = `${prefix}_SET_SEARCH_VALUE`; diff --git a/client/coral-admin/src/constants/stories.js b/client/coral-admin/src/constants/stories.js new file mode 100644 index 000000000..d1994e19a --- /dev/null +++ b/client/coral-admin/src/constants/stories.js @@ -0,0 +1,15 @@ +const prefix = 'STORIES'; + +export const FETCH_ASSETS_REQUEST = `${prefix}_FETCH_ASSETS_REQUEST`; +export const FETCH_ASSETS_SUCCESS = `${prefix}_FETCH_ASSETS_SUCCESS`; +export const FETCH_ASSETS_FAILURE = `${prefix}_FETCH_ASSETS_FAILURE`; + +export const UPDATE_ASSET_STATE_REQUEST = `${prefix}_UPDATE_ASSET_STATE_REQUEST`; +export const UPDATE_ASSET_STATE_SUCCESS = `${prefix}_UPDATE_ASSET_STATE_SUCCESS`; +export const UPDATE_ASSET_STATE_FAILURE = `${prefix}_UPDATE_ASSET_STATE_FAILURE`; + +export const UPDATE_ASSETS = `${prefix}_UPDATE_ASSETS`; + +export const SET_PAGE = `${prefix}_SET_PAGE`; +export const SET_SEARCH_VALUE = `${prefix}_SET_SEARCH_VALUE`; +export const SET_CRITERIA = `${prefix}_SET_CRITERIA`; diff --git a/client/coral-admin/src/reducers/assets.js b/client/coral-admin/src/reducers/assets.js deleted file mode 100644 index bb8751973..000000000 --- a/client/coral-admin/src/reducers/assets.js +++ /dev/null @@ -1,40 +0,0 @@ -import * as actions from '../constants/assets'; -import update from 'immutability-helper'; - -const initialState = { - byId: {}, - ids: [], - assets: [] -}; - -export default function assets (state = initialState, action) { - switch (action.type) { - case actions.FETCH_ASSETS_SUCCESS: { - const assets = action.assets.reduce((prev, curr) => { - prev[curr.id] = curr; - return prev; - }, {}); - - return update(state, { - byId: {$set: assets}, - count: {$set: action.count}, - ids: {$set: Object.keys(assets)}, - }); - } - case actions.UPDATE_ASSET_STATE_REQUEST: - return update(state, { - byId: { - [action.id]: { - closedAt: {$set: action.closedAt}, - }, - }, - }); - case actions.UPDATE_ASSETS: - return update(state, { - assets: {$set: action.assets}, - }); - default: - return state; - } -} - diff --git a/client/coral-admin/src/reducers/community.js b/client/coral-admin/src/reducers/community.js index 9f724c8d6..170804b60 100644 --- a/client/coral-admin/src/reducers/community.js +++ b/client/coral-admin/src/reducers/community.js @@ -1,8 +1,10 @@ import { - FETCH_COMMENTERS_REQUEST, - FETCH_COMMENTERS_FAILURE, - FETCH_COMMENTERS_SUCCESS, + FETCH_USERS_REQUEST, + FETCH_USERS_SUCCESS, + FETCH_USERS_FAILURE, SORT_UPDATE, + SET_PAGE, + SET_SEARCH_VALUE, SET_ROLE, SET_COMMENTER_STATUS, SHOW_BANUSER_DIALOG, @@ -15,7 +17,8 @@ const initialState = { community: {}, isFetchingPeople: false, errorPeople: '', - accounts: [], + users: [], + searchValue: '', fieldPeople: 'created_at', ascPeople: false, totalPagesPeople: 0, @@ -27,19 +30,19 @@ const initialState = { export default function community (state = initialState, action) { switch (action.type) { - case FETCH_COMMENTERS_REQUEST : + case FETCH_USERS_REQUEST : return { ...state, isFetchingPeople: true, }; - case FETCH_COMMENTERS_FAILURE : + case FETCH_USERS_FAILURE : return { ...state, isFetchingPeople: false, errorPeople: action.error, }; - case FETCH_COMMENTERS_SUCCESS : { - const {accounts, type, page, count, limit, totalPages, ...rest} = action; // eslint-disable-line + case FETCH_USERS_SUCCESS : { + const {users, type, page, count, limit, totalPages, ...rest} = action; // eslint-disable-line return { ...state, isFetchingPeople: false, @@ -49,27 +52,32 @@ export default function community (state = initialState, action) { limitPeople: limit, totalPagesPeople: totalPages, ...rest, - accounts, // Sets to normal array + users, // Sets to normal array }; } + case SET_PAGE: + return { + ...state, + pagePeople: action.page, + }; case SET_ROLE : { - const commenters = state.accounts; + const commenters = state.users; const idx = commenters.findIndex((el) => el.id === action.id); commenters[idx].roles[0] = action.role; return { ...state, - accounts: commenters.map((id) => id), + users: commenters.map((id) => id), }; } case SET_COMMENTER_STATUS: { - const commenters = state.accounts; + const commenters = state.users; const idx = commenters.findIndex((el) => el.id === action.id); commenters[idx].status = action.status; return { ...state, - accounts: commenters.map((id) => id), + users: commenters.map((id) => id), }; } @@ -101,6 +109,11 @@ export default function community (state = initialState, action) { user: action.user, rejectUsernameDialog: true }; + case SET_SEARCH_VALUE: + return { + ...state, + searchValue: action.value, + }; default : return state; } diff --git a/client/coral-admin/src/reducers/index.js b/client/coral-admin/src/reducers/index.js index 372fedbae..f7bbf3a3b 100644 --- a/client/coral-admin/src/reducers/index.js +++ b/client/coral-admin/src/reducers/index.js @@ -1,5 +1,5 @@ import auth from './auth'; -import assets from './assets'; +import stories from './stories'; import dashboard from './dashboard'; import configure from './configure'; import community from './community'; @@ -17,7 +17,7 @@ export default { configure, suspendUserDialog, userDetail, - assets, + stories, community, moderation, install, diff --git a/client/coral-admin/src/reducers/stories.js b/client/coral-admin/src/reducers/stories.js new file mode 100644 index 000000000..271ec8cfe --- /dev/null +++ b/client/coral-admin/src/reducers/stories.js @@ -0,0 +1,73 @@ +import * as actions from '../constants/stories'; +import update from 'immutability-helper'; + +const initialState = { + assets: { + byId: {}, + ids: [], + assets: [] + }, + searchValue: '', + criteria: { + asc: 'false', + filter: 'all', + }, +}; + +export default function assets (state = initialState, action) { + switch (action.type) { + case actions.FETCH_ASSETS_SUCCESS: { + const assets = action.assets.reduce((prev, curr) => { + prev[curr.id] = curr; + return prev; + }, {}); + + return update(state, { + assets: { + totalPages: {$set: action.totalPages}, + page: {$set: action.page}, + byId: {$set: assets}, + count: {$set: action.count}, + ids: {$set: Object.keys(assets)}, + }, + }); + } + case actions.UPDATE_ASSET_STATE_REQUEST: + return update(state, { + assets: { + byId: { + [action.id]: { + closedAt: {$set: action.closedAt}, + }, + }, + }, + }); + case actions.UPDATE_ASSETS: + return update(state, { + assets: { + assets: {$set: action.assets}, + }, + }); + case actions.SET_PAGE: + return { + ...state, + page: action.page, + }; + case actions.SET_SEARCH_VALUE: + return { + ...state, + searchValue: action.value, + }; + case actions.SET_CRITERIA: + return { + ...state, + criteria: { + ...state.criteria, + ...action.criteria, + }, + }; + default: + return state; + } +} + diff --git a/client/coral-admin/src/routes/Community/components/Community.css b/client/coral-admin/src/routes/Community/components/Community.css new file mode 100644 index 000000000..8f7641418 --- /dev/null +++ b/client/coral-admin/src/routes/Community/components/Community.css @@ -0,0 +1,4 @@ +.container { + max-width: 1280px; + margin: 0 auto; +} \ No newline at end of file diff --git a/client/coral-admin/src/routes/Community/components/Community.js b/client/coral-admin/src/routes/Community/components/Community.js index bf300a166..3b60b357a 100644 --- a/client/coral-admin/src/routes/Community/components/Community.js +++ b/client/coral-admin/src/routes/Community/components/Community.js @@ -1,78 +1,18 @@ import React, {Component} from 'react'; - -import CommunityMenu from './CommunityMenu'; -import People from './People'; -import FlaggedAccounts from '../containers/FlaggedAccounts'; -import RejectUsernameDialog from './RejectUsernameDialog'; import PropTypes from 'prop-types'; +import styles from './Community.css'; +import People from '../containers/People'; +import CommunityMenu from './CommunityMenu'; +import RejectUsernameDialog from './RejectUsernameDialog'; +import FlaggedAccounts from '../containers/FlaggedAccounts'; -export default class Community extends Component { - - state = { - searchValue: '', - timer: null - }; - - onKeyDownHandler = (e) => { - if (e.key === 'Enter') { - e.preventDefault(); - this.search(); - } - } - - onSearchChange = (e) => { - const value = e.target.value; - this.setState((prevState) => { - prevState.searchValue = value; - clearTimeout(prevState.timer); - const fetchAccounts = this.props.fetchAccounts; - prevState.timer = setTimeout(() => { - fetchAccounts({value}); - }, 350); - return prevState; - }); - } - - onHeaderClickHandler = (sort) => { - this.props.updateSorting(sort); - this.search(); - } - - onNewPageHandler = (page) => { - this.props.newPage(page); - this.search({page}); - } - - search(query = {}) { - const {community} = this.props; - - this.props.fetchAccounts({ - value: this.state.searchValue, - field: community.fieldPeople, - asc: community.ascPeople, - ...query - }); - } - - getTabContent(searchValue, props) { - const {community} = props; - const activeTab = props.route.path === ':id' ? 'flagged' : props.route.path; +class Community extends Component { + renderTab() { + const {route, community, ...props} = this.props; + const activeTab = route.path === ':id' ? 'flagged' : route.path; if (activeTab === 'people') { - return ( - - ); + return ; } return ( @@ -82,37 +22,36 @@ export default class Community extends Component { root={this.props.root} /> ); } render() { - const {searchValue} = this.state; - const tab = this.getTabContent(searchValue, this.props); const {root: {flaggedUsernamesCount}} = this.props; return ( -
+
-
{tab}
+
+ {this.renderTab()} +
); } } Community.propTypes = { - community: PropTypes.object, - fetchAccounts: PropTypes.func, - hideRejectUsernameDialog: PropTypes.func, - updateSorting: PropTypes.func, - newPage: PropTypes.func, route: PropTypes.object, - rejectUsername: PropTypes.func, + community: PropTypes.object, + rejectUsername: PropTypes.func.isRequired, + hideRejectUsernameDialog: PropTypes.func.isRequired, data: PropTypes.object, - root: PropTypes.object + root: PropTypes.object, }; + +export default Community; diff --git a/client/coral-admin/src/routes/Community/components/People.js b/client/coral-admin/src/routes/Community/components/People.js index b0524f273..75d08429a 100644 --- a/client/coral-admin/src/routes/Community/components/People.js +++ b/client/coral-admin/src/routes/Community/components/People.js @@ -1,33 +1,28 @@ import React from 'react'; - import styles from './styles.css'; -import Table from '../containers/Table'; -import {Pager, Icon} from 'coral-ui'; +import Table from './Table'; +import {Icon} from 'coral-ui'; import EmptyCard from '../../../components/EmptyCard'; import t from 'coral-framework/services/i18n'; + import PropTypes from 'prop-types'; -const tableHeaders = [ - { - title: t('community.username_and_email'), - field: 'username' - }, - { - title: t('community.account_creation_date'), - field: 'created_at' - }, - { - title: t('community.status'), - field: 'status' - }, - { - title: t('community.newsroom_role'), - field: 'role' - } -]; +const People = (props) => { + const { + users = [], + searchValue, + onSearchChange, + onHeaderClickHandler, + onPageChange, + totalPages, + page, + setRole, + setCommenterStatus, + viewUserDetail, + } = props; + + const hasResults = !!users.length; -const People = ({commenters, searchValue, onSearchChange, ...props}) => { - const hasResults = !!commenters.length; return (
@@ -47,28 +42,33 @@ const People = ({commenters, searchValue, onSearchChange, ...props}) => { { hasResults ? : {t('community.no_results')} } - ); }; People.propTypes = { - commenters: PropTypes.array, + onHeaderClickHandler: PropTypes.func, + users: PropTypes.array, + page: PropTypes.number.isRequired, searchValue: PropTypes.string, onSearchChange: PropTypes.func, totalPages: PropTypes.number, - onNewPageHandler: PropTypes.func, + onPageChange: PropTypes.func, + setCommenterStatus: PropTypes.func.isRequired, + setRole: PropTypes.func.isRequired, + viewUserDetail: PropTypes.func.isRequired, }; export default People; diff --git a/client/coral-admin/src/routes/Community/components/Table.css b/client/coral-admin/src/routes/Community/components/Table.css index 65eda5b4c..4071d9aa9 100644 --- a/client/coral-admin/src/routes/Community/components/Table.css +++ b/client/coral-admin/src/routes/Community/components/Table.css @@ -2,14 +2,18 @@ width: 100%; border-left: none; border-right: none; +} - th { - font-size: 1.1em; - } +th.header { + font-size: 1.1em; +} - th:nth-child(2), th:nth-child(3) { - width: 100px; - } +th.header:hover { + cursor: pointer; +} + +th.header:nth-child(2), th.header:nth-child(3) { + width: 100px; } .button { diff --git a/client/coral-admin/src/routes/Community/components/Table.js b/client/coral-admin/src/routes/Community/components/Table.js index 28b20b82f..22b02cfe4 100644 --- a/client/coral-admin/src/routes/Community/components/Table.js +++ b/client/coral-admin/src/routes/Community/components/Table.js @@ -1,68 +1,96 @@ import React from 'react'; -import styles from '../components/Table.css'; +import styles from './Table.css'; import t from 'coral-framework/services/i18n'; import PropTypes from 'prop-types'; -import {Dropdown, Option} from 'coral-ui'; +import {Paginate, Dropdown, Option} from 'coral-ui'; import cn from 'classnames'; -const Table = ({headers, commenters, onHeaderClickHandler, onRoleChange, onCommenterStatusChange, viewUserDetail}) => ( -
- - - {headers.map((header, i) =>( - - ))} - - - - {commenters.map((row, i)=> ( - - - - - +const headers = [ + { + title: t('community.username_and_email'), + field: 'username' + }, + { + title: t('community.account_creation_date'), + field: 'created_at' + }, + { + title: t('community.status'), + field: 'status' + }, + { + title: t('community.newsroom_role'), + field: 'role' + } +]; + +const Table = ({users, setRole, onHeaderClickHandler, setCommenterStatus, viewUserDetail, pageCount, page, onPageChange}) => ( +
+
onHeaderClickHandler({field: header.field})}> - {header.title} -
- - {row.profiles.map(({id}) => id)} - - {row.created_at} - - onCommenterStatusChange(row.id, status)}> - - - onRoleChange(row.id, role)}> - -
+ + + {headers.map((header, i) =>( + + ))} - ))} - -
onHeaderClickHandler({field: header.field})}> + {header.title} +
+ + + {users.map((row, i)=> ( + + + + {row.profiles.map(({id}) => id)} + + + {row.created_at} + + + setCommenterStatus(row.id, status)}> + + + + setRole(row.id, role)}> + + + + ))} + + + +
); Table.propTypes = { - headers: PropTypes.array, - commenters: PropTypes.array, - onHeaderClickHandler: PropTypes.func, - onRoleChange: PropTypes.func, - onCommenterStatusChange: PropTypes.func, - viewUserDetail: PropTypes.func, + users: PropTypes.array, + onHeaderClickHandler: PropTypes.func.isRequired, + setRole: PropTypes.func.isRequired, + setCommenterStatus: PropTypes.func.isRequired, + viewUserDetail: PropTypes.func.isRequired, + pageCount: PropTypes.number.isRequired, + page: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, }; export default Table; diff --git a/client/coral-admin/src/routes/Community/containers/Community.js b/client/coral-admin/src/routes/Community/containers/Community.js index 6e20a3167..4cf9eeb8a 100644 --- a/client/coral-admin/src/routes/Community/containers/Community.js +++ b/client/coral-admin/src/routes/Community/containers/Community.js @@ -1,65 +1,21 @@ -import React, {Component} from 'react'; import {connect} from 'react-redux'; import {bindActionCreators} from 'redux'; -import PropTypes from 'prop-types'; import {compose, gql} from 'react-apollo'; import withQuery from 'coral-framework/hocs/withQuery'; import {getDefinitionName} from 'coral-framework/utils'; import {withSetUserStatus, withRejectUsername} from 'coral-framework/graphql/mutations'; import FlaggedAccounts from '../containers/FlaggedAccounts'; import FlaggedUser from '../containers/FlaggedUser'; - -import { - fetchAccounts, - updateSorting, - newPage, - hideRejectUsernameDialog -} from '../../../actions/community'; - +import {hideRejectUsernameDialog} from '../../../actions/community'; import Community from '../components/Community'; -class CommunityContainer extends Component { - - componentWillMount() { - this.props.fetchAccounts({}); - } - - render() { - return ; - } -} const mapStateToProps = (state) => ({ community: state.community, }); -CommunityContainer.propTypes = { - community: PropTypes.object, - fetchAccounts: PropTypes.func, - hideRejectUsernameDialog: PropTypes.func, - updateSorting: PropTypes.func, - newPage: PropTypes.func, - route: PropTypes.object, - rejectUsername: PropTypes.func, - data: PropTypes.object, - root: PropTypes.object -}; - const mapDispatchToProps = (dispatch) => bindActionCreators({ - fetchAccounts, hideRejectUsernameDialog, - updateSorting, - newPage, }, dispatch); const withData = withQuery(gql` @@ -89,4 +45,4 @@ export default compose( withSetUserStatus, withRejectUsername, withData -)(CommunityContainer); +)(Community); diff --git a/client/coral-admin/src/routes/Community/containers/People.js b/client/coral-admin/src/routes/Community/containers/People.js new file mode 100644 index 000000000..063eaef2f --- /dev/null +++ b/client/coral-admin/src/routes/Community/containers/People.js @@ -0,0 +1,104 @@ +import React from 'react'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import People from '../components/People'; +import PropTypes from 'prop-types'; + +import {viewUserDetail} from '../../../actions/userDetail'; + +import { + fetchUsers, + updateSorting, + setPage, + hideRejectUsernameDialog, + setCommenterStatus, + setRole, + setSearchValue, +} from '../../../actions/community'; + +class PeopleContainer extends React.Component { + timer=null; + + fetchUsers = (query = {}) => { + const {community} = this.props; + + this.props.fetchUsers({ + value: community.searchValue, + field: community.fieldPeople, + asc: community.ascPeople, + ...query + }); + } + + componentWillMount() { + this.fetchUsers(); + } + + onKeyDownHandler = (e) => { + if (e.key === 'Enter') { + e.preventDefault(); + this.fetchUsers(); + } + } + + onSearchChange = (e) => { + const value = e.target.value; + + this.props.setSearchValue(value); + clearTimeout(this.timer); + this.timer = setTimeout(() => { + this.fetchUsers({value}); + }, 350); + } + + onHeaderClickHandler = (sort) => { + this.props.updateSorting(sort); + this.fetchUsers(); + } + + onPageChange = ({selected}) => { + const page = selected + 1; + this.props.setPage(page); + this.fetchUsers({page}); + } + + render() { + return ; + } +} + +PeopleContainer.propTypes = { + setPage: PropTypes.func, + fetchUsers: PropTypes.func, + updateSorting: PropTypes.func, + setRole: PropTypes.func.isRequired, + setCommenterStatus: PropTypes.func.isRequired, + setSearchValue: PropTypes.func.isRequired, + viewUserDetail: PropTypes.func.isRequired, + community: PropTypes.object, +}; + +const mapDispatchToProps = (dispatch) => + bindActionCreators({ + setPage, + fetchUsers, + updateSorting, + hideRejectUsernameDialog, + setCommenterStatus, + setRole, + viewUserDetail, + setSearchValue, + }, dispatch); + +export default connect(null, mapDispatchToProps)(PeopleContainer); diff --git a/client/coral-admin/src/routes/Community/containers/Table.js b/client/coral-admin/src/routes/Community/containers/Table.js deleted file mode 100644 index 086a56638..000000000 --- a/client/coral-admin/src/routes/Community/containers/Table.js +++ /dev/null @@ -1,43 +0,0 @@ -import React, {Component} from 'react'; -import {connect} from 'react-redux'; -import {bindActionCreators} from 'redux'; -import {setRole, setCommenterStatus} from '../../../actions/community'; -import Table from '../components/Table'; -import {viewUserDetail} from '../../../actions/userDetail'; -import PropTypes from 'prop-types'; - -class TableContainer extends Component { - - constructor (props) { - super(props); - } - - render () { - return ; - } -} - -TableContainer.propTypes = { - setRole: PropTypes.func, - setCommenterStatus: PropTypes.func, - commenters: PropTypes.array, -}; - -const mapStateToProps = (state) => ({ - commenters: state.community.accounts, -}); - -const mapDispatchToProps = (dispatch) => - bindActionCreators({ - setCommenterStatus, - setRole, - viewUserDetail, - }, dispatch); - -export default connect(mapStateToProps, mapDispatchToProps)(TableContainer); - diff --git a/client/coral-admin/src/routes/Stories/components/Stories.js b/client/coral-admin/src/routes/Stories/components/Stories.js index 746c733d7..866defde4 100644 --- a/client/coral-admin/src/routes/Stories/components/Stories.js +++ b/client/coral-admin/src/routes/Stories/components/Stories.js @@ -1,65 +1,20 @@ import React, {Component} from 'react'; +import cn from 'classnames'; import {Link} from 'react-router'; import PropTypes from 'prop-types'; -import sortBy from 'lodash/sortBy'; -import {Dropdown, Option, Pager, Icon} from 'coral-ui'; +import {Dropdown, Option, Paginate, Icon} from 'coral-ui'; import {DataTable, TableHeader, RadioGroup, Radio} from 'react-mdl'; import t from 'coral-framework/services/i18n'; import styles from './Stories.css'; import EmptyCard from 'coral-admin/src/components/EmptyCard'; -const limit = 25; - class Stories extends Component { - state = { - search: '', - sort: 'desc', - filter: 'all', - statusMenus: {}, - timer: null, - page: 0 - } - - componentDidMount () { - this.props.fetchAssets(0, limit, '', this.state.sortBy); - } - - onSettingChange = (setting) => (e) => { - let options = this.state; - this.setState({[setting]: e.target.value}); - options[setting] = e.target.value; - this.props.fetchAssets(0, limit, options.search, options.sort, options.filter); - } - - onSearchChange = (e) => { - const search = e.target.value; - this.setState((prevState) => { - prevState.search = search; - clearTimeout(prevState.timer); - const fetchAssets = this.props.fetchAssets; - prevState.timer = setTimeout(() => { - fetchAssets(0, limit, search, this.state.sort, this.state.filter); - }, 350); - return prevState; - }); - } - renderDate = (date) => { const d = new Date(date); return `${d.getMonth() + 1}/${d.getDate()}/${d.getFullYear()}`; } - onStatusChange = async (closeStream, id) => { - try { - this.props.updateAssetState(id, closeStream ? Date.now() : null); - const {search, sort, filter, page} = this.state; - this.props.fetchAssets(page, limit, search, sort, filter); - } catch(err) { - console.error(err); - } - } - renderTitle = (title, {id}) => {title} renderStatus = (closedAt, {id}) => { @@ -67,39 +22,27 @@ class Stories extends Component { return ( this.onStatusChange(value, id)}> + onChange={(value) => this.props.onStatusChange(value, id)}> ); } - onPageClick = (page) => { - this.setState({page}); - const {search, sort, filter} = this.state; - this.props.fetchAssets((page - 1) * limit, limit, search, sort, filter); - } - render () { - const {search, sort, filter} = this.state; - const {assets} = this.props; - - const assetsIds = sortBy(assets.ids.map((id) => assets.byId[id]), 'publication_date'); - - if (this.state.sort === 'desc') { - assetsIds.reverse(); - } + const {assets, searchValue, filter, onSearchChange, onSettingChange, onPageChange, asc} = this.props; + const rows = assets.ids.map((id) => assets.byId[id]); return ( -
+
{t('streams.filter_streams')}
@@ -108,7 +51,7 @@ class Stories extends Component { name='status filter' value={filter} childContainer='div' - onChange={this.onSettingChange('filter')} + onChange={onSettingChange('filter')} className={styles.radioGroup} > {t('streams.all')} @@ -118,19 +61,19 @@ class Stories extends Component {
{t('streams.sort_by')}
- {t('streams.newest')} - {t('streams.oldest')} + {t('streams.newest')} + {t('streams.oldest')}
{ - assetsIds.length + rows.length ?
- + {t('streams.article')} {t('streams.pubdate')} @@ -139,10 +82,10 @@ class Stories extends Component { {t('streams.status')} - +
: {t('streams.empty_result')} } @@ -153,8 +96,13 @@ class Stories extends Component { Stories.propTypes = { assets: PropTypes.object, - fetchAssets: PropTypes.func, - updateAssetState: PropTypes.func, + searchValue: PropTypes.string, + asc: PropTypes.string, + filter: PropTypes.string, + onStatusChange: PropTypes.func.isRequired, + onSearchChange: PropTypes.func.isRequired, + onPageChange: PropTypes.func.isRequired, + onSettingChange: PropTypes.func.isRequired, }; export default Stories; diff --git a/client/coral-admin/src/routes/Stories/containers/Stories.js b/client/coral-admin/src/routes/Stories/containers/Stories.js index f645f310c..d91bed968 100644 --- a/client/coral-admin/src/routes/Stories/containers/Stories.js +++ b/client/coral-admin/src/routes/Stories/containers/Stories.js @@ -1,27 +1,106 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; +import PropTypes from 'prop-types'; import {bindActionCreators} from 'redux'; -import {compose} from 'react-apollo'; -import {fetchAssets, updateAssetState} from 'coral-admin/src/actions/assets'; +import {fetchAssets, updateAssetState, setPage, setSearchValue, setCriteria} from 'coral-admin/src/actions/stories'; import Stories from '../components/Stories'; class StoriesContainer extends Component { + timer=null; + + componentDidMount () { + this.fetchAssets(); + } + + onSearchChange = (e) => { + const {value} = e.target; + + this.props.setSearchValue(value); + clearTimeout(this.timer); + this.timer = setTimeout(() => { + this.fetchAssets(); + }, 350); + } + + onSettingChange = (setting) => (e) => { + const criteria = {[setting]: e.target.value}; + this.props.setCriteria(criteria); + this.fetchAssets(criteria); + } + + fetchAssets = (query) => { + const {searchValue, asc, filter, limit} = this.props; + + this.props.fetchAssets({ + value: searchValue, + asc, + filter, + limit, + ...query + }); + }; + + onStatusChange = async (closeStream, id) => { + const {updateAssetState} = this.props; + + try { + updateAssetState(id, closeStream ? Date.now() : null); + this.fetchAssets(); + } catch(err) { + console.error(err); + } + } + + onPageChange = ({selected}) => { + const page = selected + 1; + this.props.setPage(page); + this.fetchAssets({page}); + } + render () { - return ; + return ; } } -const mapStateToProps = (state) => ({ - assets: state.assets +const mapStateToProps = ({stories}) => ({ + assets: stories.assets, + searchValue: stories.searchValue, + asc: stories.criteria.asc, + filter: stories.criteria.filter, + limit: stories.criteria.limit, }); const mapDispatchToProps = (dispatch) => bindActionCreators({ + setPage, + setCriteria, + setSearchValue, fetchAssets, updateAssetState, }, dispatch); -export default compose( - connect(mapStateToProps, mapDispatchToProps), -)(StoriesContainer); +StoriesContainer.propTypes = { + assets: PropTypes.object, + searchValue: PropTypes.string, + asc: PropTypes.string, + filter: PropTypes.string, + limit: PropTypes.number, + setPage: PropTypes.func.isRequired, + setCriteria: PropTypes.func.isRequired, + setSearchValue: PropTypes.func.isRequired, + fetchAssets: PropTypes.func.isRequired, + updateAssetState: PropTypes.func.isRequired, +}; + +export default connect(mapStateToProps, mapDispatchToProps)(StoriesContainer); diff --git a/client/coral-ui/components/Pager.css b/client/coral-ui/components/Pager.css deleted file mode 100644 index 42559d11d..000000000 --- a/client/coral-ui/components/Pager.css +++ /dev/null @@ -1,19 +0,0 @@ -.pager { - text-align: center; - - li { - display: inline-block; - margin-right: 5px; - color: white; - height: 30px; - text-align: center; - vertical-align: middle; - line-height: 30px; - width: 30px; - } -} - -.current { - background: #696969; - box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12); -} diff --git a/client/coral-ui/components/Pager.js b/client/coral-ui/components/Pager.js deleted file mode 100644 index 8f560593e..000000000 --- a/client/coral-ui/components/Pager.js +++ /dev/null @@ -1,43 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import styles from './Pager.css'; - -const Rows = (curr, total, onClickHandler) => Array.from(Array(total)).map((e, i) => -
  • onClickHandler(i + 1)}> - {i + 1} -
  • -); - -const Pager = ({totalPages, page, onNewPageHandler}) => ( -
    -
      - { - (totalPages > page && totalPages > 1) ? -
    • onNewPageHandler(page - 1)}> - Prev -
    • - : - null - } - {Rows(page, totalPages, onNewPageHandler)} - { - (page < totalPages && totalPages > 1) ? -
    • onNewPageHandler(page + 1)}> - Next -
    • - : - null - } -
    -
    -); - -Pager.propTypes = { - totalPages: PropTypes.number.isRequired, - page: PropTypes.number.isRequired, -}; - -export default Pager; diff --git a/client/coral-ui/components/Paginate.css b/client/coral-ui/components/Paginate.css new file mode 100644 index 000000000..813786d66 --- /dev/null +++ b/client/coral-ui/components/Paginate.css @@ -0,0 +1,33 @@ +.container { + text-align: center; +} + +.page, .previous, .next, .break { + display: inline-block; + list-style: none; + margin-right: 5px; +} + +.pageLink, .previousLink, .nextLink { + display: inline-block; + color: #696969; + cursor: pointer; + height: 30px; + text-align: center; + vertical-align: middle; + line-height: 30px; + width: 30px; + user-select: none; +} + +.previousLink, .nextLink { + font-size: 1.8em; +} + +.active { + background-color: #696969; + box-shadow: 0 2px 2px 0 rgba(0,0,0,.14), 0 3px 1px -2px rgba(0,0,0,.2), 0 1px 5px 0 rgba(0,0,0,.12); + a { + color: white; + } +} diff --git a/client/coral-ui/components/Paginate.js b/client/coral-ui/components/Paginate.js new file mode 100644 index 000000000..df19450c3 --- /dev/null +++ b/client/coral-ui/components/Paginate.js @@ -0,0 +1,35 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import ReactPaginate from 'react-paginate'; +import styles from './Paginate.css'; +import Icon from './Icon'; + +const Paginate = ({pageCount, page, onPageChange}) => ( + } + previousClassName={styles.previous} + previousLinkClassName={styles.previousLink} + nextLabel={} + nextClassName={styles.next} + nextLinkClassName={styles.nextLink} + /> +); + +Paginate.propTypes = { + page: PropTypes.number.isRequired, + pageCount: PropTypes.number.isRequired, + onPageChange: PropTypes.func.isRequired, +}; + +export default Paginate; diff --git a/client/coral-ui/index.js b/client/coral-ui/index.js index c688857a1..a2d8049f7 100644 --- a/client/coral-ui/index.js +++ b/client/coral-ui/index.js @@ -18,7 +18,7 @@ export {default as Item} from './components/Item'; export {default as Card} from './components/Card'; export {default as TextField} from './components/TextField'; export {default as Success} from './components/Success'; -export {default as Pager} from './components/Pager'; +export {default as Paginate} from './components/Paginate'; export {default as Wizard} from './components/Wizard'; export {default as WizardNav} from './components/WizardNav'; export {default as SnackBar} from './components/SnackBar'; diff --git a/nightwatch-browserstack.conf.js b/nightwatch-browserstack.conf.js index 604691c81..9e8e334c5 100644 --- a/nightwatch-browserstack.conf.js +++ b/nightwatch-browserstack.conf.js @@ -31,7 +31,7 @@ const nightwatch_config = { chrome: { desiredCapabilities: { browser: 'chrome', - browser_version: '60', + browser_version: '62', }, }, firefox: { diff --git a/nightwatch.conf.js b/nightwatch.conf.js index fddbce229..f843b881a 100644 --- a/nightwatch.conf.js +++ b/nightwatch.conf.js @@ -43,7 +43,7 @@ module.exports = { 'chrome-headless': { desiredCapabilities: { chromeOptions : { - args: ['--headless', '--disable-gpu'], + args: ['--headless', '--disable-gpu', 'window-size=1280,800'], }, }, }, diff --git a/package.json b/package.json index e406dcff8..85b6a2b05 100644 --- a/package.json +++ b/package.json @@ -160,6 +160,7 @@ "react-input-autosize": "^1.1.4", "react-mdl": "^1.7.2", "react-mdl-selectfield": "^0.2.0", + "react-paginate": "^5.0.0", "react-recaptcha": "^2.2.6", "react-redux": "^4.4.5", "react-router": "^3.0.0", diff --git a/routes/api/assets/index.js b/routes/api/assets/index.js index 1b2b0c7a9..165b2cd99 100644 --- a/routes/api/assets/index.js +++ b/routes/api/assets/index.js @@ -37,34 +37,44 @@ const FilterOpenAssets = (query, filter) => { router.get('/', authorization.needed('ADMIN', 'MODERATOR'), async (req, res, next) => { const { - limit = 20, - skip = 0, - sort = 'asc', + value = '', field = 'created_at', + page = 1, + asc = 'false', filter = 'all', - search = '' + limit = 20, } = req.query; try { + const queryOpts = { + sort: {[field]: (asc === 'true') ? 1 : -1}, + skip: (page - 1) * limit, + limit + }; + // Find all the assets. let [result, count] = await Promise.all([ // Find the actuall assets. - FilterOpenAssets(AssetsService.search({value: search}), filter) - .sort({[field]: (sort === 'asc') ? 1 : -1}) - .skip(parseInt(skip)) - .limit(parseInt(limit)), + FilterOpenAssets(AssetsService.search({value}), filter) + .sort(queryOpts.sort) + .skip(parseInt(queryOpts.skip)) + .limit(parseInt(queryOpts.limit)) + .lean(), // Get the count of actual assets. - FilterOpenAssets(AssetsService.search({value: search}), filter) + FilterOpenAssets(AssetsService.search({value}), filter) .count() ]); // Send back the asset data. res.json({ result, - count + limit: Number(limit), + count, + page: Number(page), + totalPages: Math.ceil(count / (limit === 0 ? 1 : limit)) }); } catch (e) { return next(e); diff --git a/routes/api/users/index.js b/routes/api/users/index.js index d076288e6..3199c25ab 100644 --- a/routes/api/users/index.js +++ b/routes/api/users/index.js @@ -10,25 +10,32 @@ const { ROOT_URL } = require('../../../config'); -// get a list of users. router.get('/', authorization.needed('ADMIN', 'MODERATOR'), async (req, res, next) => { + const { value = '', field = 'created_at', page = 1, asc = 'false', - limit = 50 // Total Per Page + limit = 20 // Total Per Page } = req.query; try { + const queryOpts = { + sort: {[field]: (asc === 'true') ? 1 : -1}, + skip: (page - 1) * limit, + limit + }; + let [result, count] = await Promise.all([ UsersService .search(value) - .sort({[field]: (asc === 'true') ? 1 : -1}) - .skip((page - 1) * limit) - .limit(limit), - UsersService.count() + .sort(queryOpts.sort) + .skip(parseInt(queryOpts.skip)) + .limit(parseInt(queryOpts.limit)) + .lean(), + UsersService.search(value).count() ]); res.json({ @@ -42,7 +49,6 @@ router.get('/', authorization.needed('ADMIN', 'MODERATOR'), async (req, res, nex } catch (e) { next(e); } - }); router.post('/:user_id/role', authorization.needed('ADMIN', 'MODERATOR'), async (req, res, next) => { diff --git a/services/users.js b/services/users.js index eb5ff46c7..9906ef6c0 100644 --- a/services/users.js +++ b/services/users.js @@ -656,8 +656,8 @@ module.exports = class UsersService { * Returns a count of the current users. * @return {Promise} */ - static count() { - return UserModel.count(); + static count(query = {}) { + return UserModel.count(query); } /** diff --git a/test/e2e/page_objects/admin.js b/test/e2e/page_objects/admin.js index c4fcb83c1..ffdcf8a29 100644 --- a/test/e2e/page_objects/admin.js +++ b/test/e2e/page_objects/admin.js @@ -14,5 +14,10 @@ module.exports = { 'emailInput': '.talk-admin-login-sign-in #email', 'passwordInput': '.talk-admin-login-sign-in #password', 'signInButton': '.talk-admin-login-sign-in-button', + 'storiesNav': '.talk-admin-nav-stories', + 'storiesSection': '.talk-admin-stories', + 'communityNav': '.talk-admin-nav-community', + 'communitySection': '.talk-admin-community', + 'moderationContainer': '.talk-admin-moderation-container' } }; diff --git a/test/e2e/specs/02_admin.js b/test/e2e/specs/02_admin.js index 236a1c6ca..463249eeb 100644 --- a/test/e2e/specs/02_admin.js +++ b/test/e2e/specs/02_admin.js @@ -1,6 +1,10 @@ module.exports = { '@tags': ['admin', 'login'], + beforeEach: (client) => { + // Testing Desktop + client.resizeWindow(1280, 800); + }, 'Admin logs in': (client) => { const adminPage = client.page.admin(); const {testData: {admin}} = client.globals; @@ -12,8 +16,28 @@ module.exports = { .setValue('@emailInput', admin.email) .setValue('@passwordInput', admin.password) .waitForElementVisible('@signInButton') - .click('@signInButton'); + .click('@signInButton') + .waitForElementVisible('@moderationContainer'); + }, + 'Admin goes to Stories': (client) => { + const adminPage = client.page.admin(); + + adminPage + .navigate() + .waitForElementVisible('@storiesNav') + .click('@storiesNav') + .waitForElementVisible('@storiesSection'); + }, + + 'Admin goes to Community': (client) => { + const adminPage = client.page.admin(); + + adminPage + .navigate() + .waitForElementVisible('@communityNav') + .click('@communityNav') + .waitForElementVisible('@communitySection'); }, after: (client) => { client.end(); diff --git a/test/server/routes/api/assets/index.js b/test/server/routes/api/assets/index.js index a07b154d4..0dacccbad 100644 --- a/test/server/routes/api/assets/index.js +++ b/test/server/routes/api/assets/index.js @@ -56,7 +56,7 @@ describe('/api/v1/assets', () => { it('should return assets that we search for', async () => { for (const role of ['ADMIN', 'MODERATOR']) { const res = await chai.request(app) - .get('/api/v1/assets?search=term2') + .get('/api/v1/assets?value=term2') .set(passport.inject({roles: [role]})); const body = res.body; @@ -78,7 +78,7 @@ describe('/api/v1/assets', () => { it('should not return assets that we do not search for', async () => { for (const role of ['ADMIN', 'MODERATOR']) { const res = await chai.request(app) - .get('/api/v1/assets?search=term3') + .get('/api/v1/assets?value=term3') .set(passport.inject({roles: [role]})); const body = res.body; diff --git a/yarn.lock b/yarn.lock index dc23d05f8..6ea60fc82 100644 --- a/yarn.lock +++ b/yarn.lock @@ -72,11 +72,7 @@ abab@^1.0.0, abab@^1.0.3: version "1.0.4" resolved "https://registry.yarnpkg.com/abab/-/abab-1.0.4.tgz#5faad9c2c07f60dd76770f71cf025b62a63cfd4e" -abbrev@1: - version "1.1.1" - resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.1.1.tgz#f8f2c887ad10bf67f634f005b6987fed3179aac8" - -abbrev@1.0.x: +abbrev@1, abbrev@1.0.x: version "1.0.9" resolved "https://registry.yarnpkg.com/abbrev/-/abbrev-1.0.9.tgz#91b4792588a7738c25f35dd6f63752a2f8776135" @@ -403,18 +399,12 @@ async@2.1.4: dependencies: lodash "^4.14.0" -async@2.4.1: +async@2.4.1, async@^2.1.2, async@^2.1.4: version "2.4.1" resolved "https://registry.yarnpkg.com/async/-/async-2.4.1.tgz#62a56b279c98a11d0987096a01cc3eeb8eb7bbd7" dependencies: lodash "^4.14.0" -async@^2.1.2, async@^2.1.4: - version "2.5.0" - resolved "https://registry.yarnpkg.com/async/-/async-2.5.0.tgz#843190fd6b7357a0b9e1c956edddd5ec8462b54d" - dependencies: - lodash "^4.14.0" - async@~0.9.0: version "0.9.2" resolved "https://registry.yarnpkg.com/async/-/async-0.9.2.tgz#aea74d5e61c1f899613bf64bda66d4c78f2fd17d" @@ -1933,14 +1923,10 @@ cookie@0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/cookie/-/cookie-0.3.1.tgz#e7e0a1f9ef43b4c8ba925c5c5a96e806d16873bb" -cookiejar@2.0.x: +cookiejar@2.0.x, cookiejar@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.0.6.tgz#0abf356ad00d1c5a219d88d44518046dd026acfe" -cookiejar@^2.0.6: - version "2.1.1" - resolved "https://registry.yarnpkg.com/cookiejar/-/cookiejar-2.1.1.tgz#41ad57b1b555951ec171412a81942b1e8200d34a" - copy-webpack-plugin@^4.0.0: version "4.1.1" resolved "https://registry.yarnpkg.com/copy-webpack-plugin/-/copy-webpack-plugin-4.1.1.tgz#53ae69e04955ebfa9fda411f54cbb968531d71fd" @@ -2375,14 +2361,10 @@ diff@1.4.0: version "1.4.0" resolved "https://registry.yarnpkg.com/diff/-/diff-1.4.0.tgz#7f28d2eb9ee7b15a97efd89ce63dcfdaa3ccbabf" -diff@3.2.0: +diff@3.2.0, diff@^3.1.0, diff@^3.2.0: version "3.2.0" resolved "https://registry.yarnpkg.com/diff/-/diff-3.2.0.tgz#c9ce393a4b7cbd0b058a725c93df299027868ff9" -diff@^3.1.0, diff@^3.2.0: - version "3.4.0" - resolved "https://registry.yarnpkg.com/diff/-/diff-3.4.0.tgz#b1d85507daf3964828de54b37d0d73ba67dda56c" - diffie-hellman@^5.0.0: version "5.0.2" resolved "https://registry.yarnpkg.com/diffie-hellman/-/diffie-hellman-5.0.2.tgz#b5835739270cfe26acf632099fded2a07f209e5e" @@ -2425,40 +2407,27 @@ domain-browser@^1.1.1: version "1.1.7" resolved "https://registry.yarnpkg.com/domain-browser/-/domain-browser-1.1.7.tgz#867aa4b093faa05f1de08c06f4d7b21fdf8698bc" -domelementtype@1, domelementtype@^1.3.0: - version "1.3.0" - resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" - -domelementtype@~1.1.1: +domelementtype@1, domelementtype@~1.1.1: version "1.1.3" resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.1.3.tgz#bd28773e2642881aec51544924299c5cd822185b" -domhandler@2.3: +domelementtype@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/domelementtype/-/domelementtype-1.3.0.tgz#b17aed82e8ab59e52dd9c19b1756e0fc187204c2" + +domhandler@2.3, domhandler@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.3.0.tgz#2de59a0822d5027fabff6f032c2b25a2a8abe738" dependencies: domelementtype "1" -domhandler@^2.3.0: - version "2.4.1" - resolved "https://registry.yarnpkg.com/domhandler/-/domhandler-2.4.1.tgz#892e47000a99be55bbf3774ffea0561d8879c259" - dependencies: - domelementtype "1" - -domutils@1.5, domutils@1.5.1: +domutils@1.5, domutils@1.5.1, domutils@^1.5.1: version "1.5.1" resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.5.1.tgz#dcd8488a26f563d61079e48c9f7b7e32373682cf" dependencies: dom-serializer "0" domelementtype "1" -domutils@^1.5.1: - version "1.6.2" - resolved "https://registry.yarnpkg.com/domutils/-/domutils-1.6.2.tgz#1958cc0b4c9426e9ed367fb1c8e854891b0fa3ff" - dependencies: - dom-serializer "0" - domelementtype "1" - dont-sniff-mimetype@1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/dont-sniff-mimetype/-/dont-sniff-mimetype-1.0.0.tgz#5932890dc9f4e2f19e5eb02a20026e5e5efc8f58" @@ -2890,7 +2859,7 @@ exports-loader@^0.6.4: loader-utils "^1.0.2" source-map "0.5.x" -express@4.16.0: +express@4.16.0, express@^4.12.2: version "4.16.0" resolved "https://registry.yarnpkg.com/express/-/express-4.16.0.tgz#b519638e4eb58e7178c81b498ef22f798cb2e255" dependencies: @@ -2925,41 +2894,6 @@ express@4.16.0: utils-merge "1.0.1" vary "~1.1.2" -express@^4.12.2: - version "4.16.2" - resolved "https://registry.yarnpkg.com/express/-/express-4.16.2.tgz#e35c6dfe2d64b7dca0a5cd4f21781be3299e076c" - dependencies: - accepts "~1.3.4" - array-flatten "1.1.1" - body-parser "1.18.2" - content-disposition "0.5.2" - content-type "~1.0.4" - cookie "0.3.1" - cookie-signature "1.0.6" - debug "2.6.9" - depd "~1.1.1" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.1" - finalhandler "1.1.0" - fresh "0.5.2" - merge-descriptors "1.0.1" - methods "~1.1.2" - on-finished "~2.3.0" - parseurl "~1.3.2" - path-to-regexp "0.1.7" - proxy-addr "~2.0.2" - qs "6.5.1" - range-parser "~1.2.0" - safe-buffer "5.1.1" - send "0.16.1" - serve-static "1.13.1" - setprototypeof "1.1.0" - statuses "~1.3.1" - type-is "~1.6.15" - utils-merge "1.0.1" - vary "~1.1.2" - extend@3, extend@^3.0.0, extend@~3.0.0, extend@~3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/extend/-/extend-3.0.1.tgz#a755ea7bc1adfcc5a31ce7e762dbaadc5e636444" @@ -3008,7 +2942,7 @@ fb-watchman@^2.0.0: dependencies: bser "^2.0.0" -fbjs@^0.8.1, fbjs@^0.8.16, fbjs@^0.8.9: +fbjs@^0.8.1, fbjs@^0.8.16, fbjs@^0.8.4, fbjs@^0.8.9: version "0.8.16" resolved "https://registry.yarnpkg.com/fbjs/-/fbjs-0.8.16.tgz#5e67432f550dc41b572bf55847b8aca64e5337db" dependencies: @@ -4047,7 +3981,7 @@ inflight@^1.0.4: once "^1.3.0" wrappy "1" -inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1, inherits@~2.0.3: +inherits@2, inherits@2.0.3, inherits@^2.0.1, inherits@^2.0.3, inherits@~2.0.0, inherits@~2.0.1: version "2.0.3" resolved "https://registry.yarnpkg.com/inherits/-/inherits-2.0.3.tgz#633c2c83e3da42a502f52466022480f4208261de" @@ -4185,7 +4119,7 @@ ip@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/ip/-/ip-1.0.1.tgz#c7e356cdea225ae71b36d70f2e71a92ba4e42590" -ip@^1.1.2, ip@^1.1.4: +ip@^1.1.2: version "1.1.5" resolved "https://registry.yarnpkg.com/ip/-/ip-1.1.5.tgz#bdded70114290828c0a039e72ef25f5aaec4354a" @@ -5515,9 +5449,9 @@ lowercase-keys@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/lowercase-keys/-/lowercase-keys-1.0.0.tgz#4e3366b39e7f5457e35f1324bdf6f88d0bfc7306" -lru-cache@^2.5.0: - version "2.7.3" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.7.3.tgz#6d4524e8b955f95d4f5b58851ce21dd72fb4e952" +lru-cache@^2.5.0, lru-cache@~2.6.5: + version "2.6.5" + resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" lru-cache@^4.0.1: version "4.1.1" @@ -5526,10 +5460,6 @@ lru-cache@^4.0.1: pseudomap "^1.0.2" yallist "^2.1.2" -lru-cache@~2.6.5: - version "2.6.5" - resolved "https://registry.yarnpkg.com/lru-cache/-/lru-cache-2.6.5.tgz#e56d6354148ede8d7707b58d143220fd08df0fd5" - macaddress@^0.2.8: version "0.2.8" resolved "https://registry.yarnpkg.com/macaddress/-/macaddress-0.2.8.tgz#5904dc537c39ec6dbefeae902327135fa8511f12" @@ -5707,7 +5637,7 @@ minimatch@3.0.3: dependencies: brace-expansion "^1.0.0" -minimist@0.0.8: +minimist@0.0.8, minimist@~0.0.1: version "0.0.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.8.tgz#857fcabfc3397d2625b8228262e86aa7a011b05d" @@ -5715,10 +5645,6 @@ minimist@^1.1.1, minimist@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.0.tgz#a35008b20f41383eec1fb914f4cd5df79a264284" -minimist@~0.0.1: - version "0.0.10" - resolved "https://registry.yarnpkg.com/minimist/-/minimist-0.0.10.tgz#de3f98543dbf96082be48ad1a0c7cda836301dcf" - mkdirp@0.5.1, mkdirp@0.5.x, "mkdirp@>=0.5 0", mkdirp@^0.5.1, mkdirp@~0.5.0, mkdirp@~0.5.1: version "0.5.1" resolved "https://registry.yarnpkg.com/mkdirp/-/mkdirp-0.5.1.tgz#30057438eac6cf7f8c4767f38648d6697d75c903" @@ -7115,7 +7041,7 @@ promised-io@*: version "0.3.5" resolved "https://registry.yarnpkg.com/promised-io/-/promised-io-0.3.5.tgz#4ad217bb3658bcaae9946b17a8668ecd851e1356" -prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8: +prop-types@^15.5.0, prop-types@^15.5.10, prop-types@^15.5.4, prop-types@^15.5.6, prop-types@^15.5.8, prop-types@^15.6.0: version "15.6.0" resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.6.0.tgz#ceaf083022fc46b4a35f69e13ef75aed0d639856" dependencies: @@ -7387,6 +7313,14 @@ rc@^1.0.1, rc@^1.1.6, rc@^1.1.7: minimist "^1.2.0" strip-json-comments "~2.0.1" +react-addons-create-fragment@^15.0.0: + version "15.6.2" + resolved "https://registry.yarnpkg.com/react-addons-create-fragment/-/react-addons-create-fragment-15.6.2.tgz#a394de7c2c7becd6b5475ba1b97ac472ce7c74f8" + dependencies: + fbjs "^0.8.4" + loose-envify "^1.3.1" + object-assign "^4.1.0" + react-apollo@^1.4.12: version "1.4.16" resolved "https://registry.yarnpkg.com/react-apollo/-/react-apollo-1.4.16.tgz#62a623458b67a174ff8ef25f64e7b42531518e19" @@ -7435,6 +7369,14 @@ react-mdl@^1.7.1, react-mdl@^1.7.2: lodash.isequal "^4.4.0" prop-types "^15.5.0" +react-paginate@^5.0.0: + version "5.0.0" + resolved "https://registry.yarnpkg.com/react-paginate/-/react-paginate-5.0.0.tgz#b5c12191ea81adc6d4d1b339b805e81841eaa8ea" + dependencies: + classnames "^2.2.5" + prop-types "^15.6.0" + react-addons-create-fragment "^15.0.0" + react-recaptcha@^2.2.6: version "2.3.5" resolved "https://registry.yarnpkg.com/react-recaptcha/-/react-recaptcha-2.3.5.tgz#a5db337125bb00fb13c2fa2e4ebfbe8b0cd06bb7" @@ -7466,20 +7408,13 @@ react-tagsinput@^3.17.0: version "3.18.0" resolved "https://registry.yarnpkg.com/react-tagsinput/-/react-tagsinput-3.18.0.tgz#40e036fc0f4c3d6b4689858189ab02926717a818" -react-test-renderer@15.5: +react-test-renderer@15.5, react-test-renderer@^15.5.0: version "15.5.4" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.5.4.tgz#d4ebb23f613d685ea8f5390109c2d20fbf7c83bc" dependencies: fbjs "^0.8.9" object-assign "^4.1.0" -react-test-renderer@^15.5.0: - version "15.6.2" - resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-15.6.2.tgz#d0333434fc2c438092696ca770da5ed48037efa8" - dependencies: - fbjs "^0.8.9" - object-assign "^4.1.0" - react-test-renderer@^16.0.0-0: version "16.0.0" resolved "https://registry.yarnpkg.com/react-test-renderer/-/react-test-renderer-16.0.0.tgz#9fe7b8308f2f71f29fc356d4102086f131c9cb15" @@ -7550,7 +7485,7 @@ read-pkg@^2.0.0: normalize-package-data "^2.3.2" path-type "^2.0.0" -readable-stream@1.1: +readable-stream@1.1, readable-stream@1.1.x: version "1.1.13" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.13.tgz#f6eef764f514c89e2b9e23146a75ba106756d23e" dependencies: @@ -7559,28 +7494,7 @@ readable-stream@1.1: isarray "0.0.1" string_decoder "~0.10.x" -readable-stream@1.1.x: - version "1.1.14" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-1.1.14.tgz#7cf4c54ef648e3813084c636dd2079e166c081d9" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.1" - isarray "0.0.1" - string_decoder "~0.10.x" - -readable-stream@2, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6: - version "2.3.3" - resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.3.3.tgz#368f2512d79f9d46fdfc71349ae7878bbc1eb95c" - dependencies: - core-util-is "~1.0.0" - inherits "~2.0.3" - isarray "~1.0.0" - process-nextick-args "~1.0.6" - safe-buffer "~5.1.1" - string_decoder "~1.0.3" - util-deprecate "~1.0.1" - -readable-stream@2.2.7: +readable-stream@2, readable-stream@2.2.7, readable-stream@^2.0.0, readable-stream@^2.0.1, readable-stream@^2.0.2, readable-stream@^2.0.5, readable-stream@^2.0.6, readable-stream@^2.1.4, readable-stream@^2.2.2, readable-stream@^2.2.6: version "2.2.7" resolved "https://registry.yarnpkg.com/readable-stream/-/readable-stream-2.2.7.tgz#07057acbe2467b22042d36f98c5ad507054e95b1" dependencies: @@ -7991,11 +7905,11 @@ rx@^2.4.3: version "2.5.3" resolved "https://registry.yarnpkg.com/rx/-/rx-2.5.3.tgz#21adc7d80f02002af50dae97fd9dbf248755f566" -safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0, safe-buffer@~5.1.1: +safe-buffer@5.1.1, safe-buffer@^5.0.1, safe-buffer@^5.1.0, safe-buffer@^5.1.1, safe-buffer@~5.1.0: version "5.1.1" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.1.tgz#893312af69b2123def71f57889001671eeb2c853" -samsam@1.1.2: +samsam@1.1.2, samsam@~1.1: version "1.1.2" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.2.tgz#bec11fdc83a9fda063401210e40176c3024d1567" @@ -8003,10 +7917,6 @@ samsam@1.x, samsam@^1.1.3: version "1.3.0" resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.3.0.tgz#8d1d9350e25622da30de3e44ba692b5221ab7c50" -samsam@~1.1: - version "1.1.3" - resolved "https://registry.yarnpkg.com/samsam/-/samsam-1.1.3.tgz#9f5087419b4d091f232571e7fa52e90b0f552621" - sane@^2.0.0: version "2.2.0" resolved "https://registry.yarnpkg.com/sane/-/sane-2.2.0.tgz#d6d2e2fcab00e3d283c93b912b7c3a20846f1d56" @@ -8097,24 +8007,6 @@ send@0.16.0: range-parser "~1.2.0" statuses "~1.3.1" -send@0.16.1: - version "0.16.1" - resolved "https://registry.yarnpkg.com/send/-/send-0.16.1.tgz#a70e1ca21d1382c11d0d9f6231deb281080d7ab3" - dependencies: - debug "2.6.9" - depd "~1.1.1" - destroy "~1.0.4" - encodeurl "~1.0.1" - escape-html "~1.0.3" - etag "~1.8.1" - fresh "0.5.2" - http-errors "~1.6.2" - mime "1.4.1" - ms "2.0.0" - on-finished "~2.3.0" - range-parser "~1.2.0" - statuses "~1.3.1" - serve-static@1.13.0: version "1.13.0" resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.0.tgz#810c91db800e94ba287eae6b4e06caab9fdc16f1" @@ -8124,15 +8016,6 @@ serve-static@1.13.0: parseurl "~1.3.2" send "0.16.0" -serve-static@1.13.1: - version "1.13.1" - resolved "https://registry.yarnpkg.com/serve-static/-/serve-static-1.13.1.tgz#4c57d53404a761d8f2e7c1e8a18a47dbf278a719" - dependencies: - encodeurl "~1.0.1" - escape-html "~1.0.3" - parseurl "~1.3.2" - send "0.16.1" - set-blocking@^2.0.0, set-blocking@~2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" @@ -8261,7 +8144,7 @@ sliced@1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/sliced/-/sliced-1.0.1.tgz#0b3a662b5d04c3177b1926bea82b03f837a2ef41" -smart-buffer@^1.0.13, smart-buffer@^1.0.4: +smart-buffer@^1.0.4: version "1.1.15" resolved "https://registry.yarnpkg.com/smart-buffer/-/smart-buffer-1.1.15.tgz#7f114b5b65fab3e2a35aa775bb12f0d1c649bf16" @@ -8302,20 +8185,13 @@ socks-proxy-agent@2: extend "3" socks "~1.1.5" -socks@1.1.9: +socks@1.1.9, socks@~1.1.5: version "1.1.9" resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.9.tgz#628d7e4d04912435445ac0b6e459376cb3e6d691" dependencies: ip "^1.1.2" smart-buffer "^1.0.4" -socks@~1.1.5: - version "1.1.10" - resolved "https://registry.yarnpkg.com/socks/-/socks-1.1.10.tgz#5b8b7fc7c8f341c53ed056e929b7bf4de8ba7b5a" - dependencies: - ip "^1.1.4" - smart-buffer "^1.0.13" - sort-keys@^1.0.0: version "1.1.2" resolved "https://registry.yarnpkg.com/sort-keys/-/sort-keys-1.1.2.tgz#441b6d4d346798f1b4e49e8920adfba0e543f9ad" @@ -8463,7 +8339,7 @@ string_decoder@^0.10.25, string_decoder@~0.10.x: version "0.10.31" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-0.10.31.tgz#62e203bc41766c6c28c9fc84301dab1c5310fa94" -string_decoder@~1.0.0, string_decoder@~1.0.3: +string_decoder@~1.0.0: version "1.0.3" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.0.3.tgz#0fc67d7c141825de94282dd536bec6b9bce860ab" dependencies: