Skip to content

Commit 9f46ad3

Browse files
authored
Add admin comment delete action with confirmation dialog
1 parent 5f7daf1 commit 9f46ad3

8 files changed

Lines changed: 569 additions & 352 deletions

File tree

admin/src/components/CommentRow/index.tsx

Lines changed: 66 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,30 @@
11
import { Flex, IconButton, Link, Td, Tooltip, Tr, Typography } from '@strapi/design-system';
2-
import { Eye } from '@strapi/icons';
2+
import { Eye, Trash } from '@strapi/icons';
3+
import { useIsMobile } from '@strapi/strapi/admin';
4+
import { useQueryClient } from '@tanstack/react-query';
35
import { isEmpty, isNil } from 'lodash';
4-
import { FC, SyntheticEvent, useMemo } from 'react';
6+
import { FC, SyntheticEvent, useCallback, useMemo } from 'react';
57
import { useIntl } from 'react-intl';
68
import { NavLink, useNavigate } from 'react-router-dom';
79
import { Comment } from '../../api/schemas';
810
import { useAPI } from '../../hooks/useAPI';
11+
import { useCommentMutations } from '../../hooks/useCommentMutations';
912
import { usePermissions } from '../../hooks/usePermissions';
1013
import { getMessage } from '../../utils';
1114
import { ApproveFlow } from '../ApproveFlow';
1215
import { CommentStatusBadge } from '../CommentStatusBadge';
16+
import { ConfirmationDialog } from '../ConfirmationDialog';
1317
import { IconButtonGroup } from '../IconButtonGroup';
1418
import { ReviewFlow } from '../ReviewFlow';
1519
import { UserAvatar } from '../UserAvatar';
16-
import { useIsMobile } from '@strapi/strapi/admin';
1720

1821
type Props = {
1922
readonly item: Comment;
2023
};
2124
export const CommentRow: FC<Props> = ({ item }) => {
22-
const {
23-
canAccessReports,
24-
canModerate,
25-
canReviewReports,
26-
} = usePermissions();
25+
const { canAccessReports, canModerate, canReviewReports } = usePermissions();
2726
const api = useAPI();
27+
const queryClient = useQueryClient();
2828
const navigate = useNavigate();
2929
const { formatDate } = useIntl();
3030

@@ -63,7 +63,26 @@ export const CommentRow: FC<Props> = ({ item }) => {
6363

6464
const { name, email, avatar } = item.author || {};
6565

66-
const isMobile = useIsMobile()
66+
const isMobile = useIsMobile();
67+
68+
const onDeleteSuccess = useCallback(() => {
69+
return queryClient.invalidateQueries({
70+
queryKey: api.comments.findAll.getKey(),
71+
exact: false,
72+
});
73+
}, [api.comments.findAll, queryClient]);
74+
75+
const { commentMutation } = useCommentMutations({
76+
comment: {
77+
deleteSuccess: onDeleteSuccess,
78+
},
79+
});
80+
81+
const handleDeleteClick = () => {
82+
commentMutation.delete.mutate(item.id);
83+
};
84+
85+
const shouldShowDeleteButton = canModerate && !item.removed;
6786

6887
return (
6988
<Tr>
@@ -73,20 +92,19 @@ export const CommentRow: FC<Props> = ({ item }) => {
7392
<Td maxWidth="200px">
7493
<Tooltip
7594
open={item.isAdminComment ? false : undefined}
76-
label={!item.isAdminComment ? email || getMessage('page.discover.table.header.author.email') : undefined}
95+
label={
96+
!item.isAdminComment
97+
? email || getMessage('page.discover.table.header.author.email')
98+
: undefined
99+
}
77100
align="start"
78-
side="left">
101+
side="left"
102+
>
79103
<Flex gap={2} style={{ cursor: item.isAdminComment ? 'default' : 'help' }}>
80104
{item.author && !isMobile && (
81-
<UserAvatar
82-
name={name || ''}
83-
avatar={avatar}
84-
isAdminComment={item.isAdminComment}
85-
/>
105+
<UserAvatar name={name || ''} avatar={avatar} isAdminComment={item.isAdminComment} />
86106
)}
87-
<Typography ellipsis>
88-
{name || getMessage('components.author.unknown')}
89-
</Typography>
107+
<Typography ellipsis>{name || getMessage('components.author.unknown')}</Typography>
90108
</Flex>
91109
</Tooltip>
92110
</Td>
@@ -101,14 +119,14 @@ export const CommentRow: FC<Props> = ({ item }) => {
101119
id: 'page.discover.table.cell.thread',
102120
props: { id: item.threadOf.id },
103121
},
104-
'#' + item.threadOf.id,
122+
'#' + item.threadOf.id
105123
)}
106124
</Link>
107-
) : '-'}
108-
</Td>
109-
<Td maxWidth="200px">
110-
{contentTypeLink ?? '-'}
125+
) : (
126+
'-'
127+
)}
111128
</Td>
129+
<Td maxWidth="200px">{contentTypeLink ?? '-'}</Td>
112130
<Td display={{ initial: 'none', large: 'table-cell' }}>
113131
<Typography>
114132
{formatDate(item.updatedAt || item.createdAt, {
@@ -137,10 +155,33 @@ export const CommentRow: FC<Props> = ({ item }) => {
137155
{canReviewReports && <ReviewFlow item={item} />}
138156
<IconButton
139157
onClick={onClickDetails(item.id)}
140-
label={getMessage("page.details.panel.discussion.nav.drilldown", "View")}
158+
label={getMessage('page.details.panel.discussion.nav.drilldown', 'View')}
141159
>
142160
<Eye />
143161
</IconButton>
162+
{shouldShowDeleteButton ? (
163+
<ConfirmationDialog
164+
title={getMessage('page.details.actions.comment.delete.confirmation.header')}
165+
labelConfirm={getMessage(
166+
'page.details.actions.comment.delete.confirmation.button.confirm'
167+
)}
168+
labelCancel={getMessage(
169+
'page.details.actions.comment.delete.confirmation.button.cancel'
170+
)}
171+
onConfirm={handleDeleteClick}
172+
Trigger={({ onClick }) => (
173+
<IconButton
174+
onClick={onClick}
175+
loading={commentMutation.delete.isPending}
176+
label={getMessage('page.details.actions.comment.delete', 'Delete')}
177+
>
178+
<Trash />
179+
</IconButton>
180+
)}
181+
>
182+
{getMessage('page.details.actions.comment.delete.confirmation.description')}
183+
</ConfirmationDialog>
184+
) : null}
144185
</IconButtonGroup>
145186
</Flex>
146187
</Td>

admin/src/translations/en.ts

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -37,12 +37,12 @@ const en = {
3737
'page.discover.table.cell.thread': 'Comment #{id}',
3838
'page.discover.table.action.display': 'Display',
3939
'page.discover.table.action.reports': 'Reports',
40-
'page.discover.table.empty': 'You don\'t have any comments yet.',
40+
'page.discover.table.empty': "You don't have any comments yet.",
4141
'page.discover.table.empty.search': 'No comments match the search.',
4242
'page.discover.table.filters': 'Filtering',
4343
'page.discover.table.reports.review': 'Review reports',
4444
'page.reports.header': 'Resolve reports',
45-
'page.reports.table.empty': 'You don\'t have any reports yet.',
45+
'page.reports.table.empty': "You don't have any reports yet.",
4646
'page.reports.table.empty.search': 'No reports match the search.',
4747
'page.reports.table.header.id': 'ID',
4848
'page.reports.table.header.reason': 'Reason',
@@ -77,9 +77,12 @@ const en = {
7777
'page.details.panel.discussion.warnings.reports.dialog.status.RESOLVED': 'Resolved',
7878
'page.details.panel.discussion.warnings.reports.dialog.actions': 'Actions',
7979
'page.details.panel.discussion.warnings.reports.dialog.actions.resolve': 'Resolve',
80-
'page.details.panel.discussion.warnings.reports.dialog.actions.resolve.selected': 'Resolve reports ({count})',
81-
'page.details.panel.discussion.warnings.reports.dialog.confirmation.success': 'Open report has been resolved',
82-
'page.details.panel.discussion.warnings.reports.selected.dialog.confirmation.success': 'Open reports has been resolved',
80+
'page.details.panel.discussion.warnings.reports.dialog.actions.resolve.selected':
81+
'Resolve reports ({count})',
82+
'page.details.panel.discussion.warnings.reports.dialog.confirmation.success':
83+
'Open report has been resolved',
84+
'page.details.panel.discussion.warnings.reports.selected.dialog.confirmation.success':
85+
'Open reports has been resolved',
8386
'page.details.panel.discussion.warnings.comments.header': 'Moderation: Review reported comment',
8487
'page.details.panel.discussion.status.blocked': 'Blocked',
8588
'page.details.panel.discussion.reply': 'Reply',
@@ -95,7 +98,8 @@ const en = {
9598
'page.details.panel.loading': 'Fetching entity...',
9699
'page.details.actions.comment.block': 'Block comment',
97100
'page.details.actions.comment.block.confirmation.header': 'Moderation: Block comment',
98-
'page.details.actions.comment.block.confirmation.description': 'Do you really want to block this comment?',
101+
'page.details.actions.comment.block.confirmation.description':
102+
'Do you really want to block this comment?',
99103
'page.details.actions.comment.block.confirmation.button.confirm': 'Yes, block it',
100104
'page.details.actions.comment.block.confirmation.success': 'Comment has been blocked',
101105
'page.details.actions.comment.unblock': 'Unblock comment',
@@ -107,7 +111,8 @@ const en = {
107111
'page.details.actions.comment.reports.reject': 'Reject',
108112
'page.details.actions.thread.block': 'Block thread',
109113
'page.details.actions.thread.block.confirmation.header': 'Moderation: Block thread',
110-
'page.details.actions.thread.block.confirmation.description': 'Do you really want to block whole thread? No further discussions are going to be allowed.',
114+
'page.details.actions.thread.block.confirmation.description':
115+
'Do you really want to block whole thread? No further discussions are going to be allowed.',
111116
'page.details.actions.thread.block.confirmation.button.confirm': 'Yes, block it',
112117
'page.details.actions.thread.block.confirmation.success': 'Thread has been blocked',
113118
'page.details.actions.thread.unblock': 'Unblock thread',
@@ -116,6 +121,11 @@ const en = {
116121
'page.details.actions.thread.modal.update.comment': 'Update comment',
117122
'page.details.actions.comment.approve.confirmation.success': 'Comment has been approved',
118123
'page.details.actions.comment.reject.confirmation.success': 'Comment has been rejected',
124+
'page.details.actions.comment.delete.confirmation.header': 'Moderation: Delete comment',
125+
'page.details.actions.comment.delete.confirmation.description':
126+
'Do you really want to delete this comment? This action is irreversible.',
127+
'page.details.actions.comment.delete.confirmation.button.confirm': 'Yes, delete it',
128+
'page.details.actions.comment.delete.confirmation.button.cancel': 'Cancel',
119129
'page.details.filters.label': 'View',
120130
'page.details.filters.removed.visibility': 'Show removed comments',
121131
'page.coming.soon': 'Wait for what is coming soon...',
@@ -129,44 +139,57 @@ const en = {
129139
'page.settings.section.additional': 'Additional configuration',
130140
'page.settings.section.client': 'Client portal configuration',
131141
'page.settings.section.restore': 'Restore default settings',
132-
'page.settings.section.restore.subtitle': 'Discarding all of applied settings and getting back to plugin default ones. Use reasonable.',
142+
'page.settings.section.restore.subtitle':
143+
'Discarding all of applied settings and getting back to plugin default ones. Use reasonable.',
133144
'page.settings.form.enabledCollections.label': 'Enable comments only for',
134145
'page.settings.form.enabledCollections.placeholder': 'Select one or more collection',
135-
'page.settings.form.enabledCollections.hint': 'If none is selected, all the content types are enabled',
146+
'page.settings.form.enabledCollections.hint':
147+
'If none is selected, all the content types are enabled',
136148
'page.settings.form.contentTypesSettings.label': 'Content types',
137149
'page.settings.form.contentTypesSettings.tooltip': 'Custom configuration per content type',
138150
'page.settings.form.moderatorRoles.label': 'Send significant notifications to',
139151
'page.settings.form.moderatorRoles.placeholder': 'Select one or more roles',
140-
'page.settings.form.moderatorRoles.hint': 'Roles which are going to be notified by the plugin about significant actions to perform',
152+
'page.settings.form.moderatorRoles.hint':
153+
'Roles which are going to be notified by the plugin about significant actions to perform',
141154
'page.settings.form.badWords.label': 'Bad words filtering',
142-
'page.settings.form.badWords.hint': 'If enabled, every post / update of comment is going to be checked against bad wording',
155+
'page.settings.form.badWords.hint':
156+
'If enabled, every post / update of comment is going to be checked against bad wording',
143157
'page.settings.form.gqlAuth.label': 'GraphQL queries authorization',
144-
'page.settings.form.gqlAuth.hint': 'If enabled, GraphQL API queries & mutations can be triggered only by Authenticated Strapi users. Otherwise API remains open.',
158+
'page.settings.form.gqlAuth.hint':
159+
'If enabled, GraphQL API queries & mutations can be triggered only by Authenticated Strapi users. Otherwise API remains open.',
145160
'page.settings.form.approvalFlow.label': 'Approval flow',
146-
'page.settings.form.approvalFlow.hint': 'Comments associated with content type "{name}" are going to be taken through manual approval flow',
161+
'page.settings.form.approvalFlow.hint':
162+
'Comments associated with content type "{name}" are going to be taken through manual approval flow',
147163
'page.settings.form.entryLabel.label': 'Title fields',
148-
'page.settings.form.entryLabel.placeholder': 'Select at least one or leave empty to apply defaults',
149-
'page.settings.form.entryLabel.hint': 'If left empty title rendering is going to take following ordered fields: "Title", "Subject" & "Name"',
164+
'page.settings.form.entryLabel.placeholder':
165+
'Select at least one or leave empty to apply defaults',
166+
'page.settings.form.entryLabel.hint':
167+
'If left empty title rendering is going to take following ordered fields: "Title", "Subject" & "Name"',
150168
'page.settings.form.client.url.label': 'Portal URL',
151169
'page.settings.form.client.url.hint': 'URL where your client portal is available',
152170
'page.settings.form.client.email.label': 'Communication e-mail',
153-
'page.settings.form.client.email.hint': 'E-mail address used for mailings, communication with users on behalf of portal team',
171+
'page.settings.form.client.email.hint':
172+
'E-mail address used for mailings, communication with users on behalf of portal team',
154173
'page.settings.actions.restore.confirmation.header': 'Restore default configuration',
155-
'page.settings.actions.restore.confirmation.description': 'You\'re about to restore plugin configuration to it default values. It might have destructive impact on already collected content. Do you really want to proceed?',
174+
'page.settings.actions.restore.confirmation.description':
175+
"You're about to restore plugin configuration to it default values. It might have destructive impact on already collected content. Do you really want to proceed?",
156176
'page.settings.actions.restore.confirmation.button.confirm': 'Yes, I want to restore',
157177
'page.settings.actions.restart.alert.title': 'Strapi requires restart',
158-
'page.settings.actions.restart.alert.description': 'You\'ve made a configuration changes which requires your Strapi application to be restarted to take an effect in GraphQL schema. Do it manually or by using below trigger.',
178+
'page.settings.actions.restart.alert.description':
179+
"You've made a configuration changes which requires your Strapi application to be restarted to take an effect in GraphQL schema. Do it manually or by using below trigger.",
159180
'page.settings.actions.restart.alert.close': 'Discard',
160181
'page.settings.notification.fetch.error': 'Failed to fetch configuration. Retrying...',
161182
'page.settings.notification.submit.success': 'Configuration has been saved successfully',
162183
'page.settings.notification.submit.error': 'Failed to save configuration. Try again.',
163184
'page.settings.notification.restore.success': 'Configuration has been restored successfully',
164185
'page.settings.notification.restore.error': 'Failed to restore configuration. Try again.',
165186
'page.settings.notification.restart.success': 'Application has been restarted successfully',
166-
'page.settings.notification.restart.error': 'Failed to restart your application. Try to do it manually.',
187+
'page.settings.notification.restart.error':
188+
'Failed to restart your application. Try to do it manually.',
167189
'page.settings.loading': 'Fetching configuration...',
168190
'page.settings.form.author.blockedProps.label': 'Blocked author details',
169-
'page.settings.form.author.blockedProps.hint': 'Specified properties will be filtered out from author\'s details (comma-separated)',
191+
'page.settings.form.author.blockedProps.hint':
192+
"Specified properties will be filtered out from author's details (comma-separated)",
170193
'components.confirmation.dialog.header': 'Confirmation',
171194
'components.confirmation.dialog.description': 'Do you really want to perform this action?',
172195
'components.confirmation.dialog.button.confirm': 'Yes, I do',
@@ -211,4 +234,4 @@ const en = {
211234
};
212235

213236
export type CommentsPluginTranslations = Partial<typeof en>;
214-
export default en;
237+
export default en;

0 commit comments

Comments
 (0)