From 6ef143a8a203bc0ba5be95a1779705631bb06d64 Mon Sep 17 00:00:00 2001 From: Tomasz Puch Date: Mon, 30 Aug 2021 18:23:46 +0200 Subject: [PATCH] feat: comments pre-moderation feat: comments pre-moderation --- admin/src/components/Item/index.js | 19 ++++++- admin/src/components/ItemDetails/index.js | 30 ++++++++--- .../ItemHeader/CardHeaderPending.js | 11 ++++ admin/src/components/ItemHeader/index.js | 43 ++++++++++----- admin/src/components/ItemModeration/index.js | 47 +++++++++++++++-- .../containers/DataManagerProvider/actions.js | 4 ++ .../containers/DataManagerProvider/index.js | 52 +++++++++++++++++++ admin/src/containers/DetailsView/index.js | 14 +++-- admin/src/permissions.js | 4 ++ admin/src/translations/en.json | 6 ++- admin/src/utils/constants.js | 9 ++++ config/functions/bootstrap.js | 16 +++++- config/routes.json | 26 ++++++++++ config/schema.graphql.js | 7 +++ controllers/comments.js | 17 ++++++ models/comment.settings.json | 4 ++ services/comments.js | 26 ++++++++++ services/utils/constants.js | 9 ++++ 18 files changed, 311 insertions(+), 33 deletions(-) create mode 100644 admin/src/components/ItemHeader/CardHeaderPending.js create mode 100644 admin/src/utils/constants.js create mode 100644 services/utils/constants.js diff --git a/admin/src/components/Item/index.js b/admin/src/components/Item/index.js index e471b93e..fa20c8a8 100644 --- a/admin/src/components/Item/index.js +++ b/admin/src/components/Item/index.js @@ -9,6 +9,7 @@ import CardItem from './CardItem'; import ItemFooter from '../ItemFooter'; import ItemHeader from '../ItemHeader'; import useDataManager from '../../hooks/useDataManager'; +import { APPROVAL_STATUS } from '../../utils/constants'; const Item = ({ id, @@ -26,6 +27,9 @@ const Item = ({ createdAt, updatedAt, relatedContentTypes, + onApproveCommentClick, + onRejectCommentClick, + approvalStatus, }) => { const { push } = useHistory(); const { getSearchParams } = useDataManager(); @@ -43,13 +47,17 @@ const Item = ({ }; const isAbuseReported = !isEmpty(reports); - const isItemHeaderDisplayed = blocked || blockedThread || isNew || removed || isAbuseReported; + const isPending = approvalStatus === APPROVAL_STATUS.PENDING; + const isItemHeaderDisplayed = blocked || blockedThread || isNew || removed || isAbuseReported || isPending; const headerProps = { blocked, blockedThread, isNew, isAbuseReported, isRemoved: removed, + onApproveCommentClick, + onRejectCommentClick, + approvalStatus, }; const footerProps = { @@ -67,7 +75,7 @@ const Item = ({ onClick={onClick} active={id === parsedId} > - { isItemHeaderDisplayed && () } + {isItemHeaderDisplayed && () }

{content}

@@ -87,6 +95,13 @@ Item.propTypes = { blocked: PropTypes.bool, blockedThread: PropTypes.bool, isNew: PropTypes.bool, + approvalStatus: PropTypes.oneOf([ + APPROVAL_STATUS.APPROVED, + APPROVAL_STATUS.PENDING, + APPROVAL_STATUS + ]), + onApproveCommentClick: PropTypes.func, + onRejectCommentClick: PropTypes.func, }; export default Item; diff --git a/admin/src/components/ItemDetails/index.js b/admin/src/components/ItemDetails/index.js index 75722806..537ef7f7 100644 --- a/admin/src/components/ItemDetails/index.js +++ b/admin/src/components/ItemDetails/index.js @@ -13,6 +13,7 @@ import ItemModeration from '../ItemModeration'; import ItemHeader from '../ItemHeader'; import AbuseReportsPopUp from '../AbuseReportsPopUp'; import pluginId from '../../pluginId'; +import { APPROVAL_STATUS } from "../../utils/constants"; const ItemDetails = ({ id, @@ -36,6 +37,9 @@ const ItemDetails = ({ onBlockClick, onBlockThreadClick, onAbuseReportResolve, + onApproveCommentClick, + onRejectCommentClick, + approvalStatus, }) => { const [showPopUp, setPopUpVisibility] = useState(false); @@ -58,14 +62,15 @@ const ItemDetails = ({ }; const hasThreads = (threadsCount !== undefined) && (threadsCount > 0); const isAbuseReported = !isEmpty(reports); - const isItemHeaderDisplayed = blocked || blockedThread || removed || isAbuseReported; + const isPending = approvalStatus === APPROVAL_STATUS.PENDING; + const isItemHeaderDisplayed = blocked || blockedThread || removed || isAbuseReported || isPending; const footerProps = { authorName, authorUser, related: isArray(related) ? first(related) : related, created_at: created_at || createdAt, updated_at: updated_at || updatedAt, - isDelailedView: true, + isDetailedView: true, }; const headerProps = { active, @@ -74,8 +79,9 @@ const ItemDetails = ({ isRemoved: removed, abuseReports: reports || [], isAbuseReported: !isEmpty(reports), - isDelailedView: true, + isDetailedView: true, onReportsClick: onPopUpOpen, + approvalStatus, }; const moderationProps = { id, @@ -83,6 +89,9 @@ const ItemDetails = ({ blockedThread, onBlockClick, onBlockThreadClick, + onApproveCommentClick, + onRejectCommentClick, + approvalStatus, }; const reportsPopUpProps = { blocked, @@ -105,11 +114,11 @@ const ItemDetails = ({ clickable={clickable} root={root} active={active}> - { isItemHeaderDisplayed && () } + {isItemHeaderDisplayed && () }

{content}

- { hasThreads && ( + {hasThreads && ( @@ -117,8 +126,8 @@ const ItemDetails = ({ )} - { active && !removed && () } - { (!isEmpty(reports) && active) && ( + {active && !removed && ()} + {(!isEmpty(reports) && active) && ( @@ -146,6 +155,13 @@ ItemDetails.propTypes = { onBlockClick: PropTypes.func, onBlockThreadClick: PropTypes.func, onAbuseReportResolve: PropTypes.func, + approvalStatus: PropTypes.oneOf([ + APPROVAL_STATUS.APPROVED, + APPROVAL_STATUS.PENDING, + APPROVAL_STATUS + ]), + onApproveCommentClick: PropTypes.func, + onRejectCommentClick: PropTypes.func, }; export default ItemDetails; diff --git a/admin/src/components/ItemHeader/CardHeaderPending.js b/admin/src/components/ItemHeader/CardHeaderPending.js new file mode 100644 index 00000000..1252f6b2 --- /dev/null +++ b/admin/src/components/ItemHeader/CardHeaderPending.js @@ -0,0 +1,11 @@ +import styled from 'styled-components'; + +import { colors } from 'strapi-helper-plugin'; + +import CardHeaderBlocked from './CardHeaderBlocked'; + +const CardHeaderPending = styled(CardHeaderBlocked)` + color: ${colors.blue}; +`; + +export default CardHeaderPending; diff --git a/admin/src/components/ItemHeader/index.js b/admin/src/components/ItemHeader/index.js index 64655d7a..7593d32e 100644 --- a/admin/src/components/ItemHeader/index.js +++ b/admin/src/components/ItemHeader/index.js @@ -3,24 +3,26 @@ import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { useGlobalContext, CheckPermissions } from 'strapi-helper-plugin'; -import { faLock, faStream, faAsterisk, faFire } from '@fortawesome/free-solid-svg-icons'; +import { faLock, faStream, faAsterisk, faFire, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; import Wrapper from './Wrapper'; import CardHeaderBlocked from './CardHeaderBlocked'; +import CardHeaderPending from './CardHeaderPending'; import pluginId from '../../pluginId'; import CardHeaderIndicatorsContainer from './CardHeaderIndicatorsContainer'; import CardHeaderIndicatorBlue from './CardHeaderIndicatorBlue'; import CardHeaderIndicatorRed from './CardHeaderIndicatorRed'; import CardHeaderReports from './CardHeaderReports'; import pluginPermissions from '../../permissions'; +import { APPROVAL_STATUS } from "../../utils/constants"; -const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isAbuseReported, isRemoved, abuseReports, onReportsClick }) => { +const ItemHeader = ({ active, isDetailedView, blocked, blockedThread, isNew, isAbuseReported, isRemoved, abuseReports, onReportsClick, approvalStatus }) => { const { formatMessage } = useGlobalContext(); const isBlocked = blocked || blockedThread; - const hasAnyIndicators = isNew || (isAbuseReported && !isDelailedView); + const hasAnyIndicators = isNew || (isAbuseReported && !isDetailedView); return ( - - { isBlocked && ( + + {isBlocked && ( @@ -28,7 +30,7 @@ const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isA - ) } + )} { isRemoved && ( @@ -40,8 +42,18 @@ const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isA ) } - { (isDelailedView && active) && (<> - { isAbuseReported && ( + { + approvalStatus === APPROVAL_STATUS.PENDING && ( + + + + + + + ) + } + {(isDetailedView && active) && (<> + {isAbuseReported && ( @@ -51,13 +63,13 @@ const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isA )} )} {hasAnyIndicators && - { isNew && ( + {isNew && ( - ) } + )} - { isAbuseReported && ( + {isAbuseReported && ( - ) } + )} } @@ -67,7 +79,7 @@ const ItemHeader = ({ active, isDelailedView, blocked, blockedThread, isNew, isA ItemHeader.propTypes = { active: PropTypes.bool, - isDelailedView: PropTypes.bool, + isDetailedView: PropTypes.bool, blocked: PropTypes.bool, blockedThread: PropTypes.bool, isNew: PropTypes.bool, @@ -75,6 +87,11 @@ ItemHeader.propTypes = { isAbuseReported: PropTypes.bool, abuseReports: PropTypes.array, onReportsClick: PropTypes.func, + approvalStatus: PropTypes.oneOf([ + APPROVAL_STATUS.APPROVED, + APPROVAL_STATUS.PENDING, + APPROVAL_STATUS + ]) }; export default ItemHeader; diff --git a/admin/src/components/ItemModeration/index.js b/admin/src/components/ItemModeration/index.js index 29f6b3d5..67a419a7 100644 --- a/admin/src/components/ItemModeration/index.js +++ b/admin/src/components/ItemModeration/index.js @@ -1,21 +1,51 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import PropTypes from 'prop-types'; import { useGlobalContext, CheckPermissions } from 'strapi-helper-plugin'; import { Button } from '@buffetjs/core'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; -import { faComment, faCommentSlash, faComments } from '@fortawesome/free-solid-svg-icons'; +import { faComment, faCommentSlash, faComments, faExclamationCircle } from '@fortawesome/free-solid-svg-icons'; import Wrapper from './Wrapper'; import pluginId from '../../pluginId'; import pluginPermissions from '../../permissions'; +import { APPROVAL_STATUS } from '../../utils/constants'; -const ItemModeration = ({ id, blocked, blockedThread, onBlockClick, onBlockThreadClick }) => { - +const ItemModeration = ({ id, blocked, blockedThread, onBlockClick, onBlockThreadClick, onApproveCommentClick, onRejectCommentClick, approvalStatus }) => { const { formatMessage } = useGlobalContext(); + const canBeApproved = + approvalStatus === APPROVAL_STATUS.PENDING || + approvalStatus === APPROVAL_STATUS.REJECTED; + const approvalButtonProps = useMemo( + () => + canBeApproved + ? { + onClick: onApproveCommentClick.bind(null, id), + color: 'primary', + label: formatMessage({ + id: `${pluginId}.list.item.moderation.button.comment.approval.approve`, + }), + } + : { + onClick: onRejectCommentClick.bind(null, id), + color: 'delete', + label: formatMessage({ + id: `${pluginId}.list.item.moderation.button.comment.approval.reject`, + }), + }, + [canBeApproved, id], + ); return ( - { !blockedThread && ( + {approvalStatus ? ( + +