diff --git a/client/coral-admin/config.json b/client/coral-admin/config.json deleted file mode 100644 index 156740de5..000000000 --- a/client/coral-admin/config.json +++ /dev/null @@ -1,3 +0,0 @@ -{ - "basePath": "admin" -} diff --git a/client/coral-admin/src/AppRouter.js b/client/coral-admin/src/AppRouter.js index 989316dd9..3bde5f931 100644 --- a/client/coral-admin/src/AppRouter.js +++ b/client/coral-admin/src/AppRouter.js @@ -1,25 +1,20 @@ import React from 'react'; import {Router, Route, IndexRedirect, browserHistory} from 'react-router'; -import Stories from 'containers/Stories/Stories'; -import Configure from 'containers/Configure/Configure'; -import LayoutContainer from 'containers/LayoutContainer'; -import InstallContainer from 'containers/Install/InstallContainer'; +import Configure from 'routes/Configure'; +import Dashboard from 'routes/Dashboard'; +import Install from 'routes/Install'; +import Stories from 'routes/Stories'; +import {CommunityLayout, Community} from 'routes/Community'; +import {ModerationLayout, Moderation} from 'routes/Moderation'; -import CommunityLayout from 'containers/Community/CommunityLayout'; -import CommunityContainer from 'containers/Community/CommunityContainer'; - -import ModerationLayout from 'containers/ModerationQueue/ModerationLayout'; -import ModerationContainer from 'containers/ModerationQueue/ModerationContainer'; - -import Dashboard from 'containers/Dashboard/Dashboard'; +import Layout from 'containers/Layout'; const routes = (
| onHeaderClickHandler({field: header.field})}> - {header.title} - | - ))} -|||
|---|---|---|---|
| - {row.username} - {row.profiles.map(({id}) => id)} - | -- {row.created_at} - | -
- |
-
- |
-
| onHeaderClickHandler({field: header.field})}> + {header.title} + | + ))} +|||
|---|---|---|---|
| + {row.username} + {row.profiles.map(({id}) => id)} + | ++ {row.created_at} + | +
+ |
+
+ |
+
Member since {new Date(user.created_at).toLocaleString()}
@@ -69,6 +67,3 @@ class UserDetail extends React.Component { } } -export default compose( - getUserDetail -)(UserDetail); diff --git a/client/coral-admin/src/containers/ModerationQueue/components/styles.css b/client/coral-admin/src/routes/Moderation/components/styles.css similarity index 100% rename from client/coral-admin/src/containers/ModerationQueue/components/styles.css rename to client/coral-admin/src/routes/Moderation/components/styles.css diff --git a/client/coral-admin/src/routes/Moderation/containers/Comment.js b/client/coral-admin/src/routes/Moderation/containers/Comment.js new file mode 100644 index 000000000..462040903 --- /dev/null +++ b/client/coral-admin/src/routes/Moderation/containers/Comment.js @@ -0,0 +1,56 @@ +import {gql} from 'react-apollo'; +import Comment from '../components/Comment'; +import {getSlotsFragments} from 'coral-framework/helpers/plugins'; +import withFragments from 'coral-framework/hocs/withFragments'; + +const pluginFragments = getSlotsFragments([ + 'adminCommentInfoBar', + 'adminCommentContent', + 'adminSideActions', + 'adminCommentDetailArea', +]); + +export default withFragments({ + root: gql` + fragment CoralAdmin_ModerationComment_root on RootQuery { + __typename + ${pluginFragments.spreads('root')} + } + ${pluginFragments.definitions('root')} + `, + comment: gql` + fragment CoralAdmin_ModerationComment_comment on Comment { + id + body + created_at + status + user { + id + name: username + status + } + asset { + id + title + url + } + action_summaries { + count + ... on FlagActionSummary { + reason + } + } + actions { + ... on FlagAction { + reason + message + user { + username + } + } + } + ${pluginFragments.spreads('comment')} + } + ${pluginFragments.definitions('comment')} + ` +})(Comment); diff --git a/client/coral-admin/src/routes/Moderation/containers/Moderation.js b/client/coral-admin/src/routes/Moderation/containers/Moderation.js new file mode 100644 index 000000000..63af5a6cd --- /dev/null +++ b/client/coral-admin/src/routes/Moderation/containers/Moderation.js @@ -0,0 +1,304 @@ +import React, {Component} from 'react'; +import {connect} from 'react-redux'; +import {bindActionCreators} from 'redux'; +import {compose, gql} from 'react-apollo'; +import isEqual from 'lodash/isEqual'; +import withQuery from 'coral-framework/hocs/withQuery'; +import {getDefinitionName} from 'coral-framework/utils'; +import * as notification from 'coral-admin/src/services/notification'; +import t, {timeago} from 'coral-framework/services/i18n'; + +import {withSetUserStatus, withSuspendUser, withSetCommentStatus} from 'coral-framework/graphql/mutations'; + +import {fetchSettings} from 'actions/settings'; +import {updateAssets} from 'actions/assets'; +import { + toggleModal, + singleView, + showBanUserDialog, + hideBanUserDialog, + showSuspendUserDialog, + hideSuspendUserDialog, + hideShortcutsNote, + viewUserDetail, + hideUserDetail, + setSortOrder, +} from 'actions/moderation'; + +import {Spinner} from 'coral-ui'; +import Moderation from '../components/Moderation'; +import Comment from './Comment'; + +class ModerationContainer extends Component { + componentWillMount() { + this.props.fetchSettings(); + } + + componentWillReceiveProps(nextProps) { + const {updateAssets} = this.props; + if(!isEqual(nextProps.root.assets, this.props.root.assets)) { + updateAssets(nextProps.root.assets); + } + } + + suspendUser = async (args) => { + this.props.hideSuspendUserDialog(); + try { + const result = await this.props.suspendUser(args); + if (result.data.suspendUser.errors) { + throw result.data.suspendUser.errors; + } + notification.success( + t('suspenduser.notify_suspend_until', + this.props.moderation.suspendUserDialog.username, + timeago(args.until)), + ); + const {commentStatus, commentId} = this.props.moderation.suspendUserDialog; + if (commentStatus !== 'REJECTED') { + return this.props.rejectComment({commentId}) + .then((result) => { + if (result.data.setCommentStatus.errors) { + throw result.data.setCommentStatus.errors; + } + }); + } + } + catch(err) { + notification.showMutationErrors(err); + } + }; + + banUser = ({userId}) => { + return this.props.setUserStatus({userId, status: 'BANNED'}); + } + + acceptComment = ({commentId}) => { + return this.props.setCommentStatus({commentId, status: 'ACCEPTED'}); + } + + rejectComment = ({commentId}) => { + return this.props.setCommentStatus({commentId, status: 'REJECTED'}); + } + + loadMore = ({limit = 10, cursor, sort, tab, asset_id}) => { + let variables = { + limit, + cursor, + sort, + asset_id + }; + switch(tab) { + case 'all': + variables.statuses = null; + break; + case 'accepted': + variables.statuses = ['ACCEPTED']; + break; + case 'premod': + variables.statuses = ['PREMOD']; + break; + case 'flagged': + variables.statuses = ['NONE', 'PREMOD']; + variables.action_type = 'FLAG'; + break; + case 'rejected': + variables.statuses = ['REJECTED']; + break; + } + return this.props.data.fetchMore({ + query: LOAD_MORE_QUERY, + variables, + updateQuery: (oldData, {fetchMoreResult:{comments}}) => { + return { + ...oldData, + [tab]: [ + ...oldData[tab], + ...comments + ] + }; + } + }); + }; + + render () { + const {root, data} = this.props; + + if (data.error) { + return