diff --git a/.gitignore b/.gitignore index 29f5961b2..fbc3c54d3 100644 --- a/.gitignore +++ b/.gitignore @@ -12,3 +12,5 @@ dump.rdb *.cfg .idea/ coverage/ +.tags +.tags1 diff --git a/client/coral-admin/src/actions/community.js b/client/coral-admin/src/actions/community.js index 0674926db..82cee32f4 100644 --- a/client/coral-admin/src/actions/community.js +++ b/client/coral-admin/src/actions/community.js @@ -7,7 +7,9 @@ import { SORT_UPDATE, COMMENTERS_NEW_PAGE, SET_ROLE, - SET_COMMENTER_STATUS + SET_COMMENTER_STATUS, + SHOW_BANUSER_DIALOG, + HIDE_BANUSER_DIALOG } from '../constants/community'; import coralApi from '../../../coral-framework/helpers/response'; @@ -56,3 +58,7 @@ export const setCommenterStatus = (id, status) => (dispatch) => { return dispatch({type: SET_COMMENTER_STATUS, id, status}); }); }; + +// Ban User Dialog +export const showBanUserDialog = (user) => ({type: SHOW_BANUSER_DIALOG, user}); +export const hideBanUserDialog = (showDialog) => ({type: HIDE_BANUSER_DIALOG, showDialog}); diff --git a/client/coral-admin/src/constants/community.js b/client/coral-admin/src/constants/community.js index 5d9fb4ab8..9ea4568ff 100644 --- a/client/coral-admin/src/constants/community.js +++ b/client/coral-admin/src/constants/community.js @@ -9,3 +9,6 @@ export const SET_COMMENTER_STATUS = 'SET_COMMENTER_STATUS'; 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 SHOW_BANUSER_DIALOG = 'SHOW_BANUSER_DIALOG'; +export const HIDE_BANUSER_DIALOG = 'HIDE_BANUSER_DIALOG'; diff --git a/client/coral-admin/src/containers/Community/Community.css b/client/coral-admin/src/containers/Community/Community.css index 05a268d5a..6460c50d2 100644 --- a/client/coral-admin/src/containers/Community/Community.css +++ b/client/coral-admin/src/containers/Community/Community.css @@ -1,3 +1,5 @@ +@custom-media --big-viewport (min-width: 780px); + .container { padding: 10px; display: flex; @@ -100,3 +102,188 @@ cursor: pointer; } } + +.list { + padding: 8px 0; + list-style: none; + display: block; + + &.singleView .listItem { + display: none; + } + + &.singleView .listItem.activeItem { + display: block; + height: 100%; + font-size: 1.5em; + line-height: 1.5em; + border: none; + + .actions { + position: fixed; + bottom: 60px; + left: 25%; + margin: 0 auto; + display: flex; + justify-content: space-around; + width: 50%; + margin: 0; + } + + .actionButton { + transform: scale(1.4); + } + } +} + +.listItem { + border-bottom: 1px solid #e0e0e0; + font-size: 16px; + width: 100%; + max-width: 660px; + min-width: 400px; + margin: 0 auto; + padding: 16px 14px; + position: relative; + transition: box-shadow 200ms; + + + &:hover { + box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); + } + + &:last-child { + border-bottom: none; + } + + .sideActions { + position: absolute; + right: 0; + height: 100%; + top: 0; + padding: 40px 18px; + box-sizing: border-box; + } + + .itemHeader { + display: inline; + + .author { + font-size: 24px; + min-width: 50px; + align-items: left; + margin-bottom: 15px; + } + } + + .itemBody { + display: block; + } + + .created { + color: #666; + font-size: 13px; + margin-left: 40px; + } + + .body { + margin-top: 20px; + flex: 1; + font-size: 0.88em; + color: black; + } + + .flagged { + color: rgba(255, 0, 0, .5); + padding-top: 15px; + padding-left: 10px; + } + + .flagCount{ + font-size: 12px; + color: #d32f2f; + } + +} + +.empty { + color: #444; + margin-top: 50px; + text-align: center; +} + + +@media (--big-viewport) { + .listItem { + border: 1px solid #e0e0e0; + margin-bottom: 30px; + + &:last-child { + border-bottom: 1px solid #e0e0e0; + } + + &.activeItem { + border: 2px solid #333; + } + } + +} + +.hasLinks { + color: #f00; + text-align: right; + display: flex; + align-items: center; + + i { + margin-right: 5px; + } +} + +.banned { + color: #f00; + text-align: left; + display: flex; + align-items: center; + + i { + margin-right: 5px; + } +} + +.ban { + display: block; + text-align: center; + margin-top: 5px; +} + +.banButton { + width: 114px; + letter-spacing: 1px; + + i { + vertical-align: middle; + margin-right: 10px; + font-size: 14px; + } +} + + +.actionButton { + transform: scale(.8); + margin: 0; +} + +.flaggedByCount { + display: block; + text-align: left; +} + +.flaggedBy { + display: inline; + padding: 3px; +} + +.flaggedByLabel { + font-weight: bold; +} diff --git a/client/coral-admin/src/containers/Community/CommunityContainer.js b/client/coral-admin/src/containers/Community/CommunityContainer.js index 0effb551c..7f83909b6 100644 --- a/client/coral-admin/src/containers/Community/CommunityContainer.js +++ b/client/coral-admin/src/containers/Community/CommunityContainer.js @@ -1,26 +1,27 @@ import React, {Component} from 'react'; import {connect} from 'react-redux'; -import {modUserFlaggedQuery} from 'coral-admin/src/graphql/queries'; import {compose} from 'react-apollo'; + +import {modUserFlaggedQuery} from 'coral-admin/src/graphql/queries'; +import {banUser} from '../../graphql/mutations'; + import { fetchAccounts, updateSorting, - newPage + newPage, + showBanUserDialog, + hideBanUserDialog } from '../../actions/community'; import CommunityMenu from './components/CommunityMenu'; +import BanUserDialog from './components/BanUserDialog'; + import People from './People'; import FlaggedAccounts from './FlaggedAccounts'; class CommunityContainer extends Component { - // static propTypes = { - // - // // list of actions (approve, reject, ban) associated with the users - // modActions: PropTypes.arrayOf(PropTypes.string).isRequired, - // } - constructor(props) { super(props); @@ -92,12 +93,23 @@ class CommunityContainer extends Component { } return ( - +
+ + +
); } @@ -122,10 +134,13 @@ const mapStateToProps = state => ({ }); const mapDispatchToProps = dispatch => ({ - fetchAccounts: query => dispatch(fetchAccounts(query)) + fetchAccounts: query => dispatch(fetchAccounts(query)), + showBanUserDialog: (user) => dispatch(showBanUserDialog(user)), + hideBanUserDialog: () => dispatch(hideBanUserDialog(false)) }); export default compose( connect(mapStateToProps, mapDispatchToProps), - modUserFlaggedQuery + modUserFlaggedQuery, + banUser )(CommunityContainer); diff --git a/client/coral-admin/src/containers/Community/FlaggedAccounts.js b/client/coral-admin/src/containers/Community/FlaggedAccounts.js index 28fc0e1f9..a6df24598 100644 --- a/client/coral-admin/src/containers/Community/FlaggedAccounts.js +++ b/client/coral-admin/src/containers/Community/FlaggedAccounts.js @@ -39,7 +39,8 @@ const FlaggedAccounts = ({...props}) => { return ; + index={index} + showBanUserDialog={props.showBanUserDialog}/>; }) : {lang.t('community.no-flagged-accounts')} } diff --git a/client/coral-admin/src/containers/Community/UserModerationList.css b/client/coral-admin/src/containers/Community/UserModerationList.css deleted file mode 100644 index dc0e6b57b..000000000 --- a/client/coral-admin/src/containers/Community/UserModerationList.css +++ /dev/null @@ -1,187 +0,0 @@ - -@custom-media --big-viewport (min-width: 780px); - -.list { - padding: 8px 0; - list-style: none; - display: block; - - &.singleView .listItem { - display: none; - } - - &.singleView .listItem.activeItem { - display: block; - height: 100%; - font-size: 1.5em; - line-height: 1.5em; - border: none; - - .actions { - position: fixed; - bottom: 60px; - left: 25%; - margin: 0 auto; - display: flex; - justify-content: space-around; - width: 50%; - margin: 0; - } - - .actionButton { - transform: scale(1.4); - } - } -} - -.listItem { - border-bottom: 1px solid #e0e0e0; - font-size: 16px; - width: 100%; - max-width: 660px; - min-width: 400px; - margin: 0 auto; - padding: 16px 14px; - position: relative; - transition: box-shadow 200ms; - - - &:hover { - box-shadow: 0 3px 6px rgba(0,0,0,0.16), 0 3px 6px rgba(0,0,0,0.23); - } - - &:last-child { - border-bottom: none; - } - - .sideActions { - position: absolute; - right: 0; - height: 100%; - top: 0; - padding: 40px 18px; - box-sizing: border-box; - } - - .itemHeader { - display: block; - - .author { - font-size: 24px; - min-width: 50px; - align-items: left; - margin-bottom: 15px; - } - } - - .itemBody { - display: block; - } - - .created { - color: #666; - font-size: 13px; - margin-left: 40px; - } - - .body { - margin-top: 20px; - flex: 1; - font-size: 0.88em; - color: black; - } - - .flagged { - color: rgba(255, 0, 0, .5); - padding-top: 15px; - padding-left: 10px; - } - - .flagCount{ - font-size: 12px; - color: #d32f2f; - } - -} - -.empty { - color: #444; - margin-top: 50px; - text-align: center; -} - - -@media (--big-viewport) { - .listItem { - border: 1px solid #e0e0e0; - margin-bottom: 30px; - - &:last-child { - border-bottom: 1px solid #e0e0e0; - } - - &.activeItem { - border: 2px solid #333; - } - } - -} - -.hasLinks { - color: #f00; - text-align: right; - display: flex; - align-items: center; - - i { - margin-right: 5px; - } -} - -.banned { - color: #f00; - text-align: left; - display: flex; - align-items: center; - - i { - margin-right: 5px; - } -} - -.ban { - display: block; - text-align: center; - margin-top: 5px; -} - -.banButton { - width: 114px; - letter-spacing: 1px; - - i { - vertical-align: middle; - margin-right: 10px; - font-size: 14px; - } -} - - -.actionButton { - transform: scale(.8); - margin: 0; -} - -.flaggedByCount { - display: block; - text-align: left; -} - -.flaggedBy { - display: inline; - padding: 3px; -} - -.flaggedByLabel { - font-weight: bold; -} diff --git a/client/coral-admin/src/containers/Community/components/ActionButton.js b/client/coral-admin/src/containers/Community/components/ActionButton.js new file mode 100644 index 000000000..9cd3da6e1 --- /dev/null +++ b/client/coral-admin/src/containers/Community/components/ActionButton.js @@ -0,0 +1,22 @@ +import React from 'react'; +import styles from '../Community.css'; +import BanUserButton from '../../../components/BanUserButton'; +import {FabButton} from 'coral-ui'; +import {menuActionsMap} from '../../../containers/ModerationQueue/helpers/moderationQueueActionsMap'; + +const ActionButton = ({type = '', user, ...props}) => { + if (type === 'BAN') { + return props.showBanUserDialog(user)} />; + } + + return ( + + ); +}; + +export default ActionButton; diff --git a/client/coral-admin/src/containers/Community/components/BanUserDialog.css b/client/coral-admin/src/containers/Community/components/BanUserDialog.css new file mode 100644 index 000000000..a46b9da32 --- /dev/null +++ b/client/coral-admin/src/containers/Community/components/BanUserDialog.css @@ -0,0 +1,164 @@ +.dialog { + border: none; + box-shadow: 0 9px 46px 8px rgba(0, 0, 0, 0.14), 0 11px 15px -7px rgba(0, 0, 0, 0.12), 0 24px 38px 3px rgba(0, 0, 0, 0.2); + width: 500px; + top: 50%; + transform: translateY(-50%); + height: 184px; + padding: 20px; + + h2 { + color: black; + font-size: 1.76em; + font-weight: 500; + margin: 0; + } + + h3 { + color: black; + font-size: 1.4em; + font-weight: 500; + margin: 0; + } +} + +.textField { + margin-top: 15px; +} + +.textField label { + font-size: 1.08em; + font-weight: bold; + margin-bottom: 5px; +} + +.textField input { + width: 100%; + display: block; + border: none; + outline: none; + border: 1px solid rgba(0,0,0,.12); + padding: 10px 6px; + box-sizing: border-box; + border-radius: 2px; + margin: 5px auto; +} + +.footer { + margin: 20px auto 10px; + text-align: center; +} + +.footer span { + display: block; + margin-bottom: 5px; +} + +.footer a { + color: #2c69b6; + cursor: pointer; + margin: 0 5px; +} + +.socialConnections { + margin-bottom: 20px; +} + +.signInButton { + margin-top: 10px; +} + +.close { + font-size: 20px; + line-height: 14px; + top: 10px; + right: 10px; + position: absolute; + display: block; + font-weight: bold; + color: #363636; + cursor: pointer; +} + +.close:hover { + color: #6b6b6b; +} + +input.error{ + border: solid 2px #f44336; +} + +.errorMsg, .hint { + color: grey; + font-weight: 600; + padding: 3px 0 16px; +} + +.alert { + padding: 10px; + margin-bottom: 20px; + border-radius: 2px; +} + +.alert--success { + border: solid 1px #1ec00e; + background: #cbf1b8; + color: #006900; +} + +.alert--error { + background: #FFEBEE; + color: #B71C1C; +} + +.userBox a { + color: #2c69b6; + cursor: pointer; + margin: 0px; +} + +.attention { + display: inline-block; + width: 15px; + height: 15px; + background: #B71C1C; + color: #FFEBEE; + font-weight: bolder; + padding: 4px; + vertical-align: middle; + border-radius: 20px; + box-sizing: border-box; + font-size: 9px; + line-height: 7px; + text-align: center; + margin-right: 5px; +} + +.action { + margin-top: 15px; +} + +.passwordRequestSuccess { + border: 1px solid green; + background-color: lightgreen; + padding: 10px; +} + +.passwordRequestFailure { + border: 1px solid orange; + background-color: 1px solid coral; + padding: 10px; +} + +.cancel { + margin-right: 10px; + width: 47%; +} + +.ban { + width: 47%; +} + +.buttons { + margin: 20px 0; +} diff --git a/client/coral-admin/src/containers/Community/components/BanUserDialog.js b/client/coral-admin/src/containers/Community/components/BanUserDialog.js new file mode 100644 index 000000000..25169ffee --- /dev/null +++ b/client/coral-admin/src/containers/Community/components/BanUserDialog.js @@ -0,0 +1,53 @@ +import React, {PropTypes} from 'react'; +import {Dialog} from 'coral-ui'; +import styles from './BanUserDialog.css'; + +import Button from 'coral-ui/components/Button'; + +import I18n from 'coral-framework/modules/i18n/i18n'; +import translations from '../../../translations'; +const lang = new I18n(translations); + +const BanUserDialog = ({open, handleClose, handleBanUser, user}) => ( + + × +
+
+

{lang.t('community.ban_user')}

+
+
+

{lang.t('community.are_you_sure', user.username)}

+ {lang.t('community.note')} +
+
+ + +
+
+
+); + +BanUserDialog.propTypes = { + handleBanUser: PropTypes.func.isRequired, + handleClose: PropTypes.func.isRequired, + user: PropTypes.object.isRequired, +}; + +export default BanUserDialog; diff --git a/client/coral-admin/src/components/SuspendUserModal.css b/client/coral-admin/src/containers/Community/components/SuspendUserModal.css similarity index 100% rename from client/coral-admin/src/components/SuspendUserModal.css rename to client/coral-admin/src/containers/Community/components/SuspendUserModal.css diff --git a/client/coral-admin/src/components/SuspendUserModal.js b/client/coral-admin/src/containers/Community/components/SuspendUserModal.js similarity index 100% rename from client/coral-admin/src/components/SuspendUserModal.js rename to client/coral-admin/src/containers/Community/components/SuspendUserModal.js diff --git a/client/coral-admin/src/containers/Community/components/User.js b/client/coral-admin/src/containers/Community/components/User.js index 1f18a186f..b15d3d2ae 100644 --- a/client/coral-admin/src/containers/Community/components/User.js +++ b/client/coral-admin/src/containers/Community/components/User.js @@ -1,5 +1,5 @@ import React from 'react'; -import styles from '../UserModerationList.css'; +import styles from '../Community.css'; import I18n from 'coral-framework/modules/i18n/i18n'; import translations from '../../../translations.json'; @@ -7,7 +7,7 @@ import translations from '../../../translations.json'; const lang = new I18n(translations); // import {Icon} from 'react-mdl'; -// import ActionButton from './ActionButton'; +import ActionButton from './ActionButton'; // Render a single user for the list const User = props => { @@ -19,9 +19,13 @@ const User = props => { return (userStatus === 'PENDING' || userStatus === 'BANNED') &&
  • -
    - {user.username} -
    + {user.username} +
    diff --git a/client/coral-admin/src/graphql/mutations/index.js b/client/coral-admin/src/graphql/mutations/index.js index fe3a1faf9..05e089f20 100644 --- a/client/coral-admin/src/graphql/mutations/index.js +++ b/client/coral-admin/src/graphql/mutations/index.js @@ -9,11 +9,35 @@ export const banUser = graphql(SET_USER_STATUS, { variables: { userId, status: 'BANNED' - } + }, + refetchQueries: ['Users'] }); }}), }); +export const setUserStatus = graphql(SET_USER_STATUS, { + props: ({mutate}) => ({ + acceptUser: (userId) => { + return mutate({ + variables: { + userId, + status: 'APPROVED' + }, + refetchQueries: ['modUserFlaggedQuery'] + }); + }, + rejectUser: (userId) => { + return mutate({ + variables: { + userId, + status: 'BANNED' + }, + refetchQueries: ['modUserFlaggedQuery'] + }); + } + }) +}); + export const setCommentStatus = graphql(SET_COMMENT_STATUS, { props: ({mutate}) => ({ acceptComment: ({commentId}) => { diff --git a/client/coral-admin/src/reducers/community.js b/client/coral-admin/src/reducers/community.js index 50641118c..c8868510f 100644 --- a/client/coral-admin/src/reducers/community.js +++ b/client/coral-admin/src/reducers/community.js @@ -6,7 +6,9 @@ import { FETCH_COMMENTERS_SUCCESS, SORT_UPDATE, SET_ROLE, - SET_COMMENTER_STATUS + SET_COMMENTER_STATUS, + SHOW_BANUSER_DIALOG, + HIDE_BANUSER_DIALOG } from '../constants/community'; const initialState = Map({ @@ -17,7 +19,9 @@ const initialState = Map({ fieldPeople: 'created_at', ascPeople: false, totalPagesPeople: 0, - pagePeople: 0 + pagePeople: 0, + user: Map({}), + banDialog: false }); export default function community (state = initialState, action) { @@ -63,6 +67,15 @@ export default function community (state = initialState, action) { return state .set('fieldPeople', action.sort.field) .set('ascPeople', !state.get('ascPeople')); + case HIDE_BANUSER_DIALOG: + return state + .set('banDialog', false); + case SHOW_BANUSER_DIALOG: + return state + .merge({ + user: Map(action.user), + banDialog: true + }); default : return state; } diff --git a/client/coral-admin/src/translations.json b/client/coral-admin/src/translations.json index eae4543ee..04cd8b142 100644 --- a/client/coral-admin/src/translations.json +++ b/client/coral-admin/src/translations.json @@ -17,10 +17,16 @@ "flaggedaccounts": "Flagged Usernames", "people": "People", "no-flagged-accounts": "The Account Flags queue is currently empty.", + "I don't like this username": "I don't like this username", "This user is impersonating": "Impersonation", "This looks like an ad/marketing": "Spam/Ads", "This username is offensive": "Offensive", - "Other": "Other" + "Other": "Other", + "ban_user": "Ban User?", + "are_you_sure": "Are you sure you would like to ban {0}?", + "note": "Note: Banning this user will not let them edit, comment or remove anything.", + "cancel": "Cancel", + "yes_ban_user": "Yes, Ban User" }, "modqueue": { "likes": "likes", @@ -156,10 +162,16 @@ "flaggedaccounts": "Nombres de Usuario Reportados", "people": "Gente", "no-flagged-accounts": "No hay ninguna cuenta reportada.", + "I don't like this username": "No me gusta ese nombre de usuario", "This user is impersonating": "Suplantación", "This looks like an ad/marketing": "Spam/Propaganda", "This username is offensive": "Ofensivo", - "Other": "Otros" + "Other": "Otros", + "ban_user": "Quieres suspender el Usuario?", + "are_you_sure": "Estas segura que quieres suspender a {0}?", + "note": "Nota: Suspender a este usuario no le va a permitir borrar ni editar ni comentar.", + "cancel": "Cancelar", + "yes_ban_user": "Si, Suspendan el usuario" }, "modqueue": { "likes": "gustos",