diff --git a/admin/src/Routes.tsx b/admin/src/Routes.tsx index 3a670359..69dcf36b 100644 --- a/admin/src/Routes.tsx +++ b/admin/src/Routes.tsx @@ -18,6 +18,7 @@ import RFPDetail from 'components/RFPDetail'; import Contributions from 'components/Contributions'; import ContributionForm from 'components/ContributionForm'; import ContributionDetail from 'components/ContributionDetail'; +import Moderation from 'components/Moderation'; import 'styles/style.less'; @@ -49,6 +50,7 @@ class Routes extends React.Component { + )} diff --git a/admin/src/components/Moderation/ModerationItem.less b/admin/src/components/Moderation/ModerationItem.less new file mode 100644 index 00000000..f7196bb4 --- /dev/null +++ b/admin/src/components/Moderation/ModerationItem.less @@ -0,0 +1,8 @@ +.ModerationItem { + & .ant-list-item-meta-title { + & small { + color: #868686; + font-weight: normal; + } + } +} diff --git a/admin/src/components/Moderation/ModerationItem.tsx b/admin/src/components/Moderation/ModerationItem.tsx new file mode 100644 index 00000000..df80e311 --- /dev/null +++ b/admin/src/components/Moderation/ModerationItem.tsx @@ -0,0 +1,81 @@ +import React from 'react'; +import { view } from 'react-easy-state'; +import store from 'src/store'; +import { Popconfirm, List, Avatar, Icon, message } from 'antd'; +import { Link } from 'react-router-dom'; +import { Comment } from 'src/types'; +import { formatDateSeconds } from 'util/time'; +import Markdown from '../Markdown'; +import ShowMore from 'components/ShowMore'; +import './ModerationItem.less'; + +class ModerationItem extends React.Component { + render() { + const p = this.props; + const avatarUrl = (p.author!.avatar && p.author!.avatar!.imageUrl) || undefined; + const actions = [ + + {p.hidden ? 'show' : 'hide'} + , + ]; + + return ( + + } + title={ + <> + {p.author!.displayName}{' '} + commented on{' '} + + {p.proposal && p.proposal.title} + {' '} + at {formatDateSeconds(p.dateCreated)}{' '} + {p.hidden && ( + <> + {' '} + + )} + {p.reported && ( + <> + {' '} + + )} + + } + description={ + + + + } + /> + + ); + } + private handleHide = async () => { + await store.updateComment(this.props.id, { hidden: !this.props.hidden }); + if (store.commentSaved) { + message.success( + <> + + {this.props.author!.displayName} + 's + {' '} + comment on {this.props.proposal!.title}{' '} + {this.props.hidden ? 'hidden' : 'now visible'} + , + ); + } + }; +} + +export default view(ModerationItem); diff --git a/admin/src/components/Moderation/index.tsx b/admin/src/components/Moderation/index.tsx new file mode 100644 index 00000000..d53d0964 --- /dev/null +++ b/admin/src/components/Moderation/index.tsx @@ -0,0 +1,29 @@ +import React from 'react'; +import { view } from 'react-easy-state'; +import store from 'src/store'; +import ModerationItem from './ModerationItem'; +import Pageable from 'components/Pageable'; +import { Comment } from 'src/types'; +import { commentFilters } from 'src/util/filters'; + +class Moderation extends React.Component<{}> { + render() { + const { page } = store.comments; + // NOTE: sync with /backend ... pagination.py ProposalCommentPagination.SORT_MAP + const sorts = ['CREATED:DESC', 'CREATED:ASC']; + return ( + } + handleSearch={store.fetchComments} + handleChangeQuery={store.setCommentPageParams} + handleResetQuery={store.resetCommentPageParams} + /> + ); + } +} + +export default view(Moderation); diff --git a/admin/src/components/Pageable/index.tsx b/admin/src/components/Pageable/index.tsx index b562c40f..accbbbf4 100644 --- a/admin/src/components/Pageable/index.tsx +++ b/admin/src/components/Pageable/index.tsx @@ -10,7 +10,7 @@ import './index.less'; interface OwnProps { page: PageData; - filters: Filters; + filters: null | Filters; sorts: string[]; searchPlaceholder?: string; controlsExtra?: React.ReactNode; @@ -39,7 +39,7 @@ class Pageable extends React.Component, {}> { } = this.props; const loading = !page.fetched || page.fetching; - const statusFilterMenu = ( + const statusFilterMenu = filters && ( {filters.list.map(f => ( {f.display} @@ -63,11 +63,13 @@ class Pageable extends React.Component, {}> { placeholder={searchPlaceholder} onSearch={this.handleSearch} /> - - - + {filters && ( + + + + )}