strapi-plugin-comments 2.0.0-rc.2 → 2.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/.circleci/config.yml +1 -1
  2. package/README.md +20 -4
  3. package/__mocks__/initSetup.js +53 -0
  4. package/admin/src/components/ActionButton/styles.js +10 -0
  5. package/admin/src/components/DiscussionThread/index.js +2 -6
  6. package/admin/src/components/DiscussionThreadItem/index.js +25 -68
  7. package/admin/src/components/DiscussionThreadItem/styles.js +4 -79
  8. package/admin/src/components/DiscussionThreadItemActions/index.js +82 -40
  9. package/admin/src/components/DiscussionThreadItemActions/styles.js +1 -12
  10. package/admin/src/components/DiscussionThreadItemApprovalFlowActions/index.js +2 -3
  11. package/admin/src/components/DiscussionThreadItemFooter/index.js +47 -0
  12. package/admin/src/components/DiscussionThreadItemFooter/styles.js +13 -0
  13. package/admin/src/components/DiscussionThreadItemReviewAction/index.js +131 -0
  14. package/admin/src/components/IconButton/styles.js +14 -0
  15. package/admin/src/components/ReportsReviewModal/index.js +1 -1
  16. package/admin/src/components/ReportsReviewTable/index.js +12 -6
  17. package/admin/src/components/StatusBadge/index.js +17 -0
  18. package/admin/src/components/StatusBadge/styles.js +17 -0
  19. package/admin/src/pages/Details/components/DetailsEntity/index.js +40 -24
  20. package/admin/src/pages/Details/components/DetailsFilters/index.js +8 -10
  21. package/admin/src/pages/Details/index.js +9 -15
  22. package/admin/src/pages/Discover/components/DiscoverTableRow/index.js +28 -65
  23. package/admin/src/translations/en.json +13 -15
  24. package/admin/src/utils/resolveCommentStatus.js +4 -4
  25. package/package.json +7 -5
  26. package/server/controllers/utils/__tests__/functions.test.js +2 -2
  27. package/server/graphql/types/comment-nested.js +1 -0
  28. package/server/services/__tests__/common.test.js +165 -0
  29. package/server/services/client.js +2 -3
  30. package/server/services/common.js +19 -2
  31. package/server/services/utils/__tests__/functions.test.js +9 -73
  32. package/server/services/utils/functions.js +1 -17
  33. package/server/utils/__tests__/functions.test.js +17 -0
  34. package/admin/src/components/DiscussionThreadItemMeta/index.js +0 -42
  35. package/admin/src/components/DiscussionThreadItemWarnings/index.js +0 -147
  36. package/admin/src/components/DiscussionThreadItemWarnings/styles.js +0 -7
@@ -7,12 +7,12 @@
7
7
  "nav.item.discover": "Discover",
8
8
  "nav.item.reports": "Reports",
9
9
  "nav.item.settings": "Settings",
10
- "page.common.item.status.OPEN": "OPEN",
11
- "page.common.item.status.BLOCKED": "BLOCKED",
12
- "page.common.item.status.APPROVED": "APPROVED",
13
- "page.common.item.status.TO_REVIEW": "TO REVIEW ({count, number})",
14
- "page.common.item.status.REJECTED": "REJECTED",
15
- "page.common.item.status.REMOVED": "REMOVED BY AUTHOR",
10
+ "page.common.item.status.OPEN": "Open",
11
+ "page.common.item.status.BLOCKED": "Blocked",
12
+ "page.common.item.status.APPROVED": "Approved",
13
+ "page.common.item.status.TO_REVIEW": "To review ({count, number})",
14
+ "page.common.item.status.REJECTED": "Rejected",
15
+ "page.common.item.status.REMOVED": "Removed by author",
16
16
  "page.updates.header": "Recent updates",
17
17
  "page.discover.header": "Discover threads",
18
18
  "page.discover.header.count": "entries found",
@@ -47,17 +47,17 @@
47
47
  "page.details.panel.discussion.warnings.reports.dialog.content": "Content",
48
48
  "page.details.panel.discussion.warnings.reports.dialog.createdAt": "Issue date",
49
49
  "page.details.panel.discussion.warnings.reports.dialog.status": "Status",
50
- "page.details.panel.discussion.warnings.reports.dialog.status.OPEN": "OPEN",
51
- "page.details.panel.discussion.warnings.reports.dialog.status.RESOLVED": "RESOLVED",
50
+ "page.details.panel.discussion.warnings.reports.dialog.status.OPEN": "Open",
51
+ "page.details.panel.discussion.warnings.reports.dialog.status.RESOLVED": "Resolved",
52
52
  "page.details.panel.discussion.warnings.reports.dialog.actions": "Actions",
53
53
  "page.details.panel.discussion.warnings.reports.dialog.actions.resolve": "Resolve",
54
54
  "page.details.panel.discussion.warnings.reports.dialog.confirmation.success": "Open report has been resolved",
55
- "page.details.panel.discussion.status.blocked": "BLOCKED",
55
+ "page.details.panel.discussion.status.blocked": "Blocked",
56
56
  "page.details.panel.entity": "Entity details",
57
57
  "page.details.panel.status": "Discussion is",
58
- "page.details.panel.status.open": "OPEN",
59
- "page.details.panel.status.blocked": "BLOCKED",
60
- "page.details.panel.status.pending": "PENDING",
58
+ "page.details.panel.status.open": "Open",
59
+ "page.details.panel.status.blocked": "Blocked",
60
+ "page.details.panel.status.pending": "Pending",
61
61
  "page.details.panel.loading": "Fetching entity...",
62
62
  "page.details.actions.comment.block": "Block comment",
63
63
  "page.details.actions.comment.block.confirmation.header": "Moderation: Block comment",
@@ -77,9 +77,7 @@
77
77
  "page.details.actions.comment.approve.confirmation.success": "Comment has been approved",
78
78
  "page.details.actions.comment.reject.confirmation.success": "Comment has been rejected",
79
79
  "page.details.filters.label": "View",
80
- "page.details.filters.removed.visibility": "Comments visibility",
81
- "page.details.filters.removed.show": "Show removed",
82
- "page.details.filters.removed.hide": "Hide removed",
80
+ "page.details.filters.removed.visibility": "Show removed comments",
83
81
  "page.coming.soon": "Wait for what is coming soon...",
84
82
  "compontents.confirmation.dialog.header": "Confirmation",
85
83
  "compontents.confirmation.dialog.description": "Do you really want to perform this action?",
@@ -4,10 +4,6 @@ import { COMMENT_STATUS } from './constants';
4
4
  const resolveCommentStatus = ({ removed, blocked, blockedThread, approvalStatus, reviewFlowEnabled }) => {
5
5
  const gotApprovalFlow = !isNil(approvalStatus);
6
6
 
7
- if (reviewFlowEnabled) {
8
- return COMMENT_STATUS.TO_REVIEW;
9
- }
10
-
11
7
  if (removed) {
12
8
  return COMMENT_STATUS.REMOVED;
13
9
  }
@@ -15,6 +11,10 @@ const resolveCommentStatus = ({ removed, blocked, blockedThread, approvalStatus,
15
11
  if (blocked || blockedThread) {
16
12
  return COMMENT_STATUS.BLOCKED;
17
13
  }
14
+
15
+ if (reviewFlowEnabled) {
16
+ return COMMENT_STATUS.TO_REVIEW;
17
+ }
18
18
 
19
19
  if (gotApprovalFlow) {
20
20
  const status = approvalStatus.toUpperCase();
package/package.json CHANGED
@@ -1,10 +1,11 @@
1
1
  {
2
2
  "name": "strapi-plugin-comments",
3
- "version": "2.0.0-rc.2",
3
+ "version": "2.0.0",
4
4
  "description": "Strapi - Comments plugin",
5
5
  "strapi": {
6
6
  "name": "comments",
7
- "description": "Comments & audience engagement",
7
+ "displayName": "Comments",
8
+ "description": "Powerful Strapi based comments moderation tool for you and your users",
8
9
  "kind": "plugin"
9
10
  },
10
11
  "scripts": {
@@ -16,15 +17,16 @@
16
17
  "url": "https://github.com/VirtusLab-Open-Source/strapi-plugin-comments"
17
18
  },
18
19
  "dependencies": {
19
- "@strapi/utils": "^4.0.6",
20
- "@strapi/plugin-graphql": "^4.0.6",
20
+ "@strapi/utils": "^4.0.7",
21
+ "@strapi/plugin-graphql": "^4.0.7",
21
22
  "bad-words": "^3.0.4"
22
23
  },
23
24
  "devDependencies": {
24
25
  "codecov": "^3.7.2",
25
26
  "jest": "^26.0.1",
26
27
  "jest-cli": "^26.0.1",
27
- "jest-styled-components": "^7.0.2"
28
+ "jest-styled-components": "^7.0.2",
29
+ "koa": "2.13.3"
28
30
  },
29
31
  "peerDependencies": {
30
32
  "@strapi/strapi": "4.x"
@@ -1,6 +1,6 @@
1
- const PluginError = require('../../../services/utils/error');
1
+ const PluginError = require('../../../utils/error');
2
2
  const ctx = require('koa/lib/context');
3
- const { parseParams, throwError } = require('../functions');
3
+ const { throwError } = require('../functions');
4
4
 
5
5
  jest.mock
6
6
 
@@ -6,6 +6,7 @@ module.exports = ({ nexus }) => nexus.objectType({
6
6
  t.boolean("blocked")
7
7
  t.boolean("blockedThread")
8
8
  t.string("approvalStatus")
9
+ t.boolean("removed")
9
10
  t.field("threadOf", { type: 'CommentSingle' })
10
11
  t.list.field("children", { type: 'CommentNested' })
11
12
  t.field("author", { type: 'CommentAuthor' })
@@ -0,0 +1,165 @@
1
+ const PluginError = require('../../utils/error');
2
+ const { getPluginService } = require('../../utils/functions');
3
+
4
+ jest.mock
5
+
6
+
7
+ const setup = function(config) {
8
+ Object.defineProperty(global, 'strapi', {
9
+ value: require('../../../__mocks__/initSetup')(config),
10
+ writable: true,
11
+ });
12
+ }
13
+
14
+ afterEach(() => {
15
+ Object.defineProperty(global, 'strapi', {});
16
+ })
17
+
18
+ describe('Test Comments service functions utils', () => {
19
+
20
+ describe('Get plugin config', () => {
21
+ beforeEach(() => setup({
22
+ test: 'sample',
23
+ }));
24
+
25
+ test('Should return config value', () => {
26
+ expect(getPluginService('common').getConfig('test')).toEqual('sample');
27
+ });
28
+
29
+ test('Should return default config prop value if not set', () => {
30
+ expect(getPluginService('common').getConfig('another', 'defaultValue')).toEqual('defaultValue');
31
+ });
32
+ });
33
+
34
+ describe('Validate user context', () => {
35
+ beforeEach(() => setup());
36
+
37
+ test('Should context be valid', () => {
38
+ expect(getPluginService('common').isValidUserContext({ id: 1 })).toEqual(true);
39
+ });
40
+
41
+ test('Should context be invalid', () => {
42
+ expect(getPluginService('common').isValidUserContext({ })).toEqual(false);
43
+ });
44
+
45
+ test('Should use fallback', () => {
46
+ expect(getPluginService('common').isValidUserContext()).toEqual(true);
47
+ });
48
+ });
49
+
50
+ describe('Sanitize comments entity', () => {
51
+ beforeEach(() => setup());
52
+
53
+ const sample = {
54
+ id: 1,
55
+ content: 'Test text',
56
+ blocked: false,
57
+ removed: true,
58
+ };
59
+
60
+ const authorSample = {
61
+ id: 1,
62
+ username: 'Joe Doe',
63
+ email: 'test@example.com',
64
+ password: 'xxx',
65
+ };
66
+
67
+ const authorGenericSample = {
68
+ authorId: 1,
69
+ authorName: 'Joe Doe',
70
+ authorEmail: 'test@example.com',
71
+ };
72
+
73
+ const outputAuthorSample = {
74
+ id: 1,
75
+ name: 'Joe Doe',
76
+ email: 'test@example.com',
77
+ };
78
+
79
+ test('Should return exact the same value', () => {
80
+ const input = { ...sample };
81
+ const output = getPluginService('common').sanitizeCommentEntity(input);
82
+ expect(output).toHaveProperty('id', input.id);
83
+ expect(output).toHaveProperty('content', input.content);
84
+ expect(output).toHaveProperty('blocked', input.blocked);
85
+ expect(output).toHaveProperty('removed', input.removed);
86
+ });
87
+
88
+ test('Should return entity with \'author\' property for generic author', () => {
89
+ const input = {
90
+ ...sample,
91
+ ...authorGenericSample,
92
+ };
93
+ const output = getPluginService('common').sanitizeCommentEntity(input);
94
+
95
+ expect(output).toHaveProperty('author');
96
+ expect(output).toHaveProperty('author.id', input.authorId);
97
+ expect(output).toHaveProperty('author.id', outputAuthorSample.id);
98
+ expect(output).toHaveProperty('author.name', input.authorName);
99
+ expect(output).toHaveProperty('author.name', outputAuthorSample.name);
100
+ expect(output).toHaveProperty('author.email', input.authorEmail);
101
+ expect(output).toHaveProperty('author.email', outputAuthorSample.email);
102
+
103
+ expect(output).not.toHaveProperty('authorId');
104
+ expect(output).not.toHaveProperty('authorName');
105
+ expect(output).not.toHaveProperty('authorEmail');
106
+ });
107
+
108
+ test('Should return entity with \'author\' property for Strapi author', () => {
109
+ const input = {
110
+ ...sample,
111
+ authorUser: authorSample,
112
+ };
113
+ const output = getPluginService('common').sanitizeCommentEntity(input);
114
+
115
+ expect(output).toHaveProperty('author');
116
+ expect(output).toHaveProperty('author.id', input.authorUser.id);
117
+ expect(output).toHaveProperty('author.id', outputAuthorSample.id);
118
+ expect(output).toHaveProperty('author.name', input.authorUser.username);
119
+ expect(output).toHaveProperty('author.name', outputAuthorSample.name);
120
+ expect(output).toHaveProperty('author.email', input.authorUser.email);
121
+ expect(output).toHaveProperty('author.email', outputAuthorSample.email);
122
+
123
+ expect(output).not.toHaveProperty('authorUser');
124
+
125
+ expect(output).not.toHaveProperty('author.password');
126
+ });
127
+ });
128
+
129
+ describe('Bad Words filtering', () => {
130
+ beforeEach(() => setup());
131
+
132
+ const text = 'Lorem ipsum dolor sit fuck amet';
133
+
134
+ test('Should find bad words usage and throw error PluginError', () => {
135
+ expect.assertions(7);
136
+ try {
137
+ getPluginService('common').checkBadWords(text);
138
+ } catch (e) {
139
+ expect(getPluginService('common').getConfig('badWords')).toEqual(undefined);
140
+ expect(e).toBeInstanceOf(PluginError);
141
+ expect(e).toHaveProperty('status', 400);
142
+ expect(e).toHaveProperty('name', 'Strapi:Plugin:Comments');
143
+ expect(e).toHaveProperty('message', 'Bad language used! Please polite your comment...');
144
+ expect(e).toHaveProperty('payload');
145
+ expect(e.payload).toEqual(expect.objectContaining({
146
+ content: {
147
+ original: text,
148
+ filtered: text.replace('fuck', '****'),
149
+ }
150
+ }));
151
+ }
152
+ });
153
+ });
154
+
155
+ describe('Bad Words filtering', () => {
156
+ beforeEach(() => setup({ badWords: false }));
157
+
158
+ const text = 'Lorem ipsum dolor sit fuck amet';
159
+
160
+ test('Should skip bad words filtering because of configuration change', () => {
161
+ expect(getPluginService('common').getConfig('badWords')).toEqual(false);
162
+ expect(getPluginService('common').checkBadWords(text)).toEqual(text);
163
+ });
164
+ });
165
+ });
@@ -8,7 +8,6 @@ const {
8
8
  isEqualEntity,
9
9
  getModelUid,
10
10
  getRelatedGroups,
11
- checkBadWords,
12
11
  resolveUserContextError,
13
12
  } = require('./utils/functions');
14
13
  const { APPROVAL_STATUS, REGEX } = require('./../utils/constants')
@@ -58,7 +57,7 @@ module.exports = ({ strapi }) => ({
58
57
  throw resolveUserContextError(user);
59
58
  }
60
59
 
61
- if (checkBadWords(content) && singleRelationFulfilled) {
60
+ if (this.getCommonService().checkBadWords(content) && singleRelationFulfilled) {
62
61
  const { author = {}, ...rest } = data;
63
62
  let authorData = {};
64
63
  if (validContext && user) {
@@ -128,7 +127,7 @@ module.exports = ({ strapi }) => ({
128
127
  }
129
128
 
130
129
  if (isEqualEntity(existingEntity, data, user) && content) {
131
- if (checkBadWords(content)) {
130
+ if (this.getCommonService().checkBadWords(content)) {
132
131
  const entity = await strapi.db.query(getModelUid('comment')).update({
133
132
  where: { id },
134
133
  data: { content },
@@ -1,7 +1,7 @@
1
1
  'use strict';
2
2
 
3
+ const BadWordsFilter = require('bad-words');
3
4
  const { isArray, isNumber, isObject, isNil, first, parseInt } = require('lodash');
4
- // const { sanitizeEntity } = require('strapi-utils');
5
5
  const PluginError = require('./../utils/error');
6
6
  const {
7
7
  getModelUid,
@@ -22,7 +22,8 @@ module.exports = ({ strapi }) => ({
22
22
  if (prop && isArray(prop)) {
23
23
  queryProp = prop.join('.');
24
24
  }
25
- return strapi.config.get(`plugin.comments${ queryProp ? '.' + queryProp : ''}`) || defaultValue;
25
+ const result = strapi.config.get(`plugin.comments${ queryProp ? '.' + queryProp : ''}`);
26
+ return isNil(result) ? defaultValue : result;
26
27
  },
27
28
 
28
29
  // Find comments in the flat structure
@@ -170,4 +171,20 @@ module.exports = ({ strapi }) => ({
170
171
  isValidUserContext(user) {
171
172
  return user ? !isNil(user?.id) : true;
172
173
  },
174
+
175
+ checkBadWords(content) {
176
+ const config = this.getConfig('badWords', true);
177
+ if (config) {
178
+ const filter = new BadWordsFilter(isObject(config) ? config : undefined);
179
+ if (content && filter.isProfane(content)) {
180
+ throw new PluginError(400, 'Bad language used! Please polite your comment...', {
181
+ content: {
182
+ original: content,
183
+ filtered: content && filter.clean(content),
184
+ },
185
+ });
186
+ }
187
+ }
188
+ return content;
189
+ },
173
190
  });
@@ -1,86 +1,22 @@
1
- const PluginError = require('../error');
2
- const { checkBadWords, extractMeta, isValidUserContext, resolveUserContextError } = require('../functions');
1
+ const PluginError = require('../../../utils/error');
2
+ const { resolveUserContextError, getModelUid } = require('../functions');
3
3
 
4
4
  beforeEach(() => {
5
5
  Object.defineProperty(global, 'strapi', {
6
- value: {
7
- plugins: {
8
- comments: {
9
- package: require('../../../package.json'),
10
- services: {
11
- comments: require('../../comments'),
12
- },
13
- models: {
14
- comment: require('../../../models/comment.settings.json'),
15
- report: require('../../../models/report.settings.json'),
16
- }
17
- }
18
- },
19
- config: {
20
- plugins: {
21
- comments: {
22
- enableUsers: false,
23
- }
24
- }
25
- }
26
- },
6
+ value: require('../../../../__mocks__/initSetup')(),
27
7
  writable: true,
28
8
  });
29
9
  })
30
10
 
31
- describe('Test Comments service functions utils', () => {
32
-
33
- describe('Bad Words filtering', () => {
34
- const text = 'Lorem ipsum dolor sit fuck amet';
35
-
36
- test('Should find bad words usage and throw error PluginError', () => {
37
- expect.assertions(6);
38
- try {
39
- checkBadWords(text);
40
- } catch (e) {
41
- expect(e).toBeInstanceOf(PluginError);
42
- expect(e).toHaveProperty('status', 400);
43
- expect(e).toHaveProperty('name', 'Strapi:Plugin:Comments');
44
- expect(e).toHaveProperty('message', 'Bad language used! Please polite your comment...');
45
- expect(e).toHaveProperty('payload');
46
- expect(e.payload).toEqual(expect.objectContaining({
47
- content: {
48
- original: text,
49
- filtered: text.replace('fuck', '****'),
50
- }
51
- }));
52
- }
53
- });
54
-
55
- test('Should skip bad words filtering because of configuration change', () => {
56
- global.strapi.config.plugins.comments.badWords = false;
57
-
58
- expect(checkBadWords(text)).toEqual(text);
59
- });
60
- });
61
-
62
- describe('Extracting metadata', () => {
63
- test('Should extract plugin metadata properly', () => {
64
- const { model, service, plugin, pluginName } = extractMeta(strapi.plugins);
65
-
66
- expect(model).toHaveProperty('info.name', 'comment');
67
- expect(service).toHaveProperty('findAll');
68
- expect(Object.keys(plugin)).toEqual(expect.arrayContaining(['package', 'services', 'models']));
69
- expect(pluginName).toBe('comments');
70
- });
71
- });
11
+ describe('Test service functions utils', () => {
72
12
 
73
- describe('Validating user context', () => {
74
- test('Context should be skipped based on config', () => {
75
- global.strapi.config.plugins.comments.enableUsers = false;
76
- expect(isValidUserContext(undefined)).toEqual(true);
77
- expect(isValidUserContext({ id: 1 })).toEqual(true);
13
+ describe('Getting models uid\'s ', () => {
14
+ test('Should get comments uid', () => {
15
+ expect(getModelUid('comment')).toBe('plugins::comments.comment');
78
16
  });
79
17
 
80
- test('Context should be verified based on input', () => {
81
- global.strapi.config.plugins.comments.enableUsers = true;
82
- expect(isValidUserContext(undefined)).toEqual(false);
83
- expect(isValidUserContext({ id: 1 })).toEqual(true);
18
+ test('Should get report uid', () => {
19
+ expect(getModelUid('comment-report')).toBe('plugins::comments.comment-report');
84
20
  });
85
21
  });
86
22
 
@@ -1,7 +1,6 @@
1
- const BadWordsFilter = require('bad-words');
2
1
  const PluginError = require('./../../utils/error');
3
2
  const { REGEX } = require('./../../utils/constants')
4
- const { first, get, isObject, isEmpty } = require('lodash');
3
+ const { first, isObject, isEmpty } = require('lodash');
5
4
 
6
5
  const buildNestedStructure = (
7
6
  entities,
@@ -63,21 +62,6 @@ module.exports = {
63
62
  reports: (item.reports || []).filter(report => !report.resolved),
64
63
  } : item),
65
64
 
66
- checkBadWords: content => {
67
- const config = get(strapi.config, 'plugins.comments.badWords', true);
68
- if (config) {
69
- const filter = new BadWordsFilter(isObject(config) ? config : undefined);
70
- if (content && filter.isProfane(content)) {
71
- throw new PluginError(400, 'Bad language used! Please polite your comment...', {
72
- content: {
73
- original: content,
74
- filtered: content && filter.clean(content),
75
- },
76
- });
77
- }
78
- }
79
- return content;
80
- },
81
65
  convertContentTypeNameToSlug: str => {
82
66
  const plainConversion = str.replace(/[A-Z]/g, letter => `-${letter.toLowerCase()}`);
83
67
  return first(plainConversion) === '-' ? plainConversion.slice(1, plainConversion.length) : plainConversion;
@@ -0,0 +1,17 @@
1
+ const { getPluginService } = require('../functions');
2
+
3
+ beforeEach(() => {
4
+ Object.defineProperty(global, 'strapi', {
5
+ value: require('../../../__mocks__/initSetup')(),
6
+ writable: true,
7
+ });
8
+ })
9
+
10
+ describe('Test plugin functions utils', () => {
11
+
12
+ describe('Get plugin service', () => {
13
+ test('Should get common service', () => {
14
+ expect(getPluginService('common')).toHaveProperty('findAllFlat');
15
+ });
16
+ });
17
+ });
@@ -1,42 +0,0 @@
1
- /**
2
- *
3
- * Entity Details
4
- *
5
- */
6
-
7
- import React from 'react';
8
- import PropTypes from 'prop-types';
9
- import { useIntl } from 'react-intl';
10
- import { Box } from '@strapi/design-system/Box';
11
- import { Flex } from '@strapi/design-system/Flex';
12
- import { Stack } from '@strapi/design-system/Stack';
13
- import { Typography } from '@strapi/design-system/Typography';
14
-
15
- const DiscussionThreadItemMeta = ({ author, createdAt, updatedAt }) => {
16
-
17
- const { formatDate } = useIntl();
18
-
19
- const dateTime = formatDate(updatedAt || createdAt, { dateStyle: 'short', timeStyle: 'short' });
20
- const { name } = author;
21
-
22
- return (<Flex as={Box} paddingTop={2} direction="row">
23
- <Stack size={2} horizontal style={{ flexGrow: 1, justifyContent: 'space-between'}}>
24
- <Typography variant="pi" fontWeight="bold">{ name }</Typography>
25
- <Typography variant="pi">{ dateTime }</Typography>
26
- </Stack>
27
- </Flex>);
28
- };
29
-
30
- DiscussionThreadItemMeta.propTypes = {
31
- author: PropTypes.shape({
32
- id: PropTypes.oneOfType(PropTypes.string, PropTypes.number).isRequired,
33
- name: PropTypes.string.isRequired,
34
- email: PropTypes.string,
35
- avatar: PropTypes.string,
36
- }).isRequired,
37
- createdAt: PropTypes.string.isRequired,
38
- updatedAt: PropTypes.string,
39
- };
40
-
41
- export default DiscussionThreadItemMeta;
42
-