@strapi/plugin-users-permissions 4.20.5 → 5.0.0-alpha.1

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 (112) hide show
  1. package/admin/src/components/FormModal/index.jsx +1 -2
  2. package/admin/src/components/Permissions/reducer.js +1 -1
  3. package/admin/src/components/UsersPermissions/reducer.js +1 -1
  4. package/admin/src/index.js +15 -32
  5. package/admin/src/pages/AdvancedSettings/index.jsx +72 -112
  6. package/admin/src/pages/AdvancedSettings/utils/layout.js +20 -35
  7. package/admin/src/pages/AdvancedSettings/utils/schema.js +5 -2
  8. package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +47 -74
  9. package/admin/src/pages/EmailTemplates/components/EmailTable.jsx +4 -5
  10. package/admin/src/pages/EmailTemplates/index.jsx +25 -55
  11. package/admin/src/pages/EmailTemplates/utils/schema.js +18 -6
  12. package/admin/src/pages/Providers/index.jsx +91 -108
  13. package/admin/src/pages/Providers/utils/forms.js +11 -11
  14. package/admin/src/pages/Roles/constants.js +3 -3
  15. package/admin/src/pages/Roles/hooks/usePlugins.js +4 -4
  16. package/admin/src/pages/Roles/index.jsx +9 -18
  17. package/admin/src/pages/Roles/pages/CreatePage.jsx +20 -28
  18. package/admin/src/pages/Roles/pages/EditPage.jsx +25 -37
  19. package/admin/src/pages/Roles/pages/ListPage/components/TableBody.jsx +23 -28
  20. package/admin/src/pages/Roles/pages/ListPage/index.jsx +73 -42
  21. package/admin/src/translations/en.json +1 -1
  22. package/admin/src/utils/prefixPluginTranslations.js +13 -0
  23. package/dist/_chunks/EditViewPage-kgrZ8rEg-6k5dfk_x.js +84412 -0
  24. package/dist/_chunks/EditViewPage-kgrZ8rEg-6k5dfk_x.js.map +1 -0
  25. package/dist/_chunks/EditViewPage-kgrZ8rEg-GlayP0Uq.mjs +84382 -0
  26. package/dist/_chunks/EditViewPage-kgrZ8rEg-GlayP0Uq.mjs.map +1 -0
  27. package/dist/_chunks/Helmet-d9JljxUo.js +1010 -0
  28. package/dist/_chunks/Helmet-d9JljxUo.js.map +1 -0
  29. package/dist/_chunks/Helmet-kyJ1Zklj.mjs +1008 -0
  30. package/dist/_chunks/Helmet-kyJ1Zklj.mjs.map +1 -0
  31. package/dist/_chunks/ListViewPage-BNB0ptO7-TUQO_9Hj.js +1617 -0
  32. package/dist/_chunks/ListViewPage-BNB0ptO7-TUQO_9Hj.js.map +1 -0
  33. package/dist/_chunks/ListViewPage-BNB0ptO7-t1ra9JlI.mjs +1594 -0
  34. package/dist/_chunks/ListViewPage-BNB0ptO7-t1ra9JlI.mjs.map +1 -0
  35. package/dist/_chunks/ReviewWorkflowsColumn-56Z6l-FH-3Dq1lGu9.js +33 -0
  36. package/dist/_chunks/ReviewWorkflowsColumn-56Z6l-FH-3Dq1lGu9.js.map +1 -0
  37. package/dist/_chunks/ReviewWorkflowsColumn-56Z6l-FH-mpkuW-HV.mjs +33 -0
  38. package/dist/_chunks/ReviewWorkflowsColumn-56Z6l-FH-mpkuW-HV.mjs.map +1 -0
  39. package/dist/_chunks/constants-evLWZCaJ-0QLv9QPI.mjs +190 -0
  40. package/dist/_chunks/constants-evLWZCaJ-0QLv9QPI.mjs.map +1 -0
  41. package/dist/_chunks/constants-evLWZCaJ-dGs71EWl.js +209 -0
  42. package/dist/_chunks/constants-evLWZCaJ-dGs71EWl.js.map +1 -0
  43. package/dist/_chunks/{en-m608rMZx.js → en-TaNIVnDO.js} +2 -2
  44. package/dist/_chunks/en-TaNIVnDO.js.map +1 -0
  45. package/dist/_chunks/{en-CE3wEy_c.mjs → en-jvJ-d-Qq.mjs} +2 -2
  46. package/dist/_chunks/en-jvJ-d-Qq.mjs.map +1 -0
  47. package/dist/_chunks/{index-XqdaO5WZ.js → index-6E51D69B.js} +149 -149
  48. package/dist/_chunks/index-6E51D69B.js.map +1 -0
  49. package/dist/_chunks/index-BGIcvvEB.mjs +260 -0
  50. package/dist/_chunks/index-BGIcvvEB.mjs.map +1 -0
  51. package/dist/_chunks/{index-6Kdo3KXv.js → index-Bg2Rf_5y.js} +112 -154
  52. package/dist/_chunks/index-Bg2Rf_5y.js.map +1 -0
  53. package/dist/_chunks/index-LpFmy25n.js +279 -0
  54. package/dist/_chunks/index-LpFmy25n.js.map +1 -0
  55. package/dist/_chunks/{index-a9oKDd3C.mjs → index-R05CeNXG.mjs} +106 -148
  56. package/dist/_chunks/index-R05CeNXG.mjs.map +1 -0
  57. package/dist/_chunks/index-YFPS5vYF-ZGkR3L1g.js +16558 -0
  58. package/dist/_chunks/index-YFPS5vYF-ZGkR3L1g.js.map +1 -0
  59. package/dist/_chunks/index-YFPS5vYF-cugkJcLS.mjs +16533 -0
  60. package/dist/_chunks/index-YFPS5vYF-cugkJcLS.mjs.map +1 -0
  61. package/dist/_chunks/{index-ethhTEkj.mjs → index-aEKi1Qb9.mjs} +39 -36
  62. package/dist/_chunks/index-aEKi1Qb9.mjs.map +1 -0
  63. package/dist/_chunks/{index-rryiT0-Z.mjs → index-hG66XSuA.mjs} +131 -130
  64. package/dist/_chunks/index-hG66XSuA.mjs.map +1 -0
  65. package/dist/_chunks/{index-iNtwnT3f.mjs → index-xt3l4qU9.mjs} +35 -35
  66. package/dist/_chunks/index-xt3l4qU9.mjs.map +1 -0
  67. package/dist/_chunks/{index-O9AAUvyy.js → index-yKMi8hKt.js} +36 -36
  68. package/dist/_chunks/index-yKMi8hKt.js.map +1 -0
  69. package/dist/_chunks/{index-1uupZmu0.js → index-ylhaoJtw.js} +43 -40
  70. package/dist/_chunks/index-ylhaoJtw.js.map +1 -0
  71. package/dist/_chunks/useSyncRbac-83vFRiaG-YY4KQcAU.js +57 -0
  72. package/dist/_chunks/useSyncRbac-83vFRiaG-YY4KQcAU.js.map +1 -0
  73. package/dist/_chunks/useSyncRbac-83vFRiaG-ov11t-T1.mjs +39 -0
  74. package/dist/_chunks/useSyncRbac-83vFRiaG-ov11t-T1.mjs.map +1 -0
  75. package/dist/admin/index.js +1 -2
  76. package/dist/admin/index.js.map +1 -1
  77. package/dist/admin/index.mjs +1 -2
  78. package/dist/admin/index.mjs.map +1 -1
  79. package/dist/style.css +84 -0
  80. package/package.json +13 -13
  81. package/server/bootstrap/grant-config.js +9 -0
  82. package/server/bootstrap/index.js +2 -39
  83. package/server/content-types/user/index.js +0 -1
  84. package/server/controllers/auth.js +24 -53
  85. package/server/controllers/content-manager-user.js +24 -28
  86. package/server/controllers/role.js +1 -1
  87. package/server/controllers/user.js +5 -5
  88. package/server/middlewares/rateLimit.js +1 -1
  89. package/server/register.js +1 -1
  90. package/server/services/jwt.js +3 -3
  91. package/server/services/permission.js +3 -7
  92. package/server/services/providers-registry.js +15 -0
  93. package/server/services/providers.js +10 -5
  94. package/server/services/role.js +15 -13
  95. package/server/services/user.js +28 -14
  96. package/server/services/users-permissions.js +12 -10
  97. package/server/utils/sanitize/sanitizers.js +2 -2
  98. package/admin/src/pages/Roles/pages/ListPage/utils/api.js +0 -30
  99. package/dist/_chunks/en-CE3wEy_c.mjs.map +0 -1
  100. package/dist/_chunks/en-m608rMZx.js.map +0 -1
  101. package/dist/_chunks/index-1uupZmu0.js.map +0 -1
  102. package/dist/_chunks/index-6Kdo3KXv.js.map +0 -1
  103. package/dist/_chunks/index-O9AAUvyy.js.map +0 -1
  104. package/dist/_chunks/index-Un-J-cxQ.js +0 -320
  105. package/dist/_chunks/index-Un-J-cxQ.js.map +0 -1
  106. package/dist/_chunks/index-X0yw_GgN.mjs +0 -301
  107. package/dist/_chunks/index-X0yw_GgN.mjs.map +0 -1
  108. package/dist/_chunks/index-XqdaO5WZ.js.map +0 -1
  109. package/dist/_chunks/index-a9oKDd3C.mjs.map +0 -1
  110. package/dist/_chunks/index-ethhTEkj.mjs.map +0 -1
  111. package/dist/_chunks/index-iNtwnT3f.mjs.map +0 -1
  112. package/dist/_chunks/index-rryiT0-Z.mjs.map +0 -1
@@ -12,20 +12,13 @@ import {
12
12
  TextInput,
13
13
  Typography,
14
14
  } from '@strapi/design-system';
15
- import {
16
- CheckPagePermissions,
17
- Form,
18
- SettingsPageTitle,
19
- useFetchClient,
20
- useNotification,
21
- useOverlayBlocker,
22
- useTracking,
23
- } from '@strapi/helper-plugin';
24
15
  import { Check } from '@strapi/icons';
25
- import { Formik } from 'formik';
16
+ import { Page, useTracking, useNotification, useFetchClient } from '@strapi/strapi/admin';
17
+ import { Formik, Form } from 'formik';
18
+ import { Helmet } from 'react-helmet';
26
19
  import { useIntl } from 'react-intl';
27
20
  import { useMutation } from 'react-query';
28
- import { useHistory } from 'react-router-dom';
21
+ import { useNavigate } from 'react-router-dom';
29
22
 
30
23
  import UsersPermissions from '../../../components/UsersPermissions';
31
24
  import { PERMISSIONS } from '../../../constants';
@@ -35,9 +28,8 @@ import { usePlugins } from '../hooks/usePlugins';
35
28
 
36
29
  export const CreatePage = () => {
37
30
  const { formatMessage } = useIntl();
38
- const toggleNotification = useNotification();
39
- const { goBack } = useHistory();
40
- const { lockApp, unlockApp } = useOverlayBlocker();
31
+ const { toggleNotification } = useNotification();
32
+ const navigate = useNavigate();
41
33
  const { isLoading: isLoadingPlugins, permissions, routes } = usePlugins();
42
34
  const { trackUsage } = useTracking();
43
35
  const permissionsRef = React.useRef();
@@ -45,11 +37,11 @@ export const CreatePage = () => {
45
37
  const mutation = useMutation((body) => post(`/users-permissions/roles`, body), {
46
38
  onError() {
47
39
  toggleNotification({
48
- type: 'warning',
49
- message: {
40
+ type: 'danger',
41
+ message: formatMessage({
50
42
  id: 'notification.error',
51
43
  defaultMessage: 'An error occurred',
52
- },
44
+ }),
53
45
  });
54
46
  },
55
47
 
@@ -58,34 +50,34 @@ export const CreatePage = () => {
58
50
 
59
51
  toggleNotification({
60
52
  type: 'success',
61
- message: {
53
+ message: formatMessage({
62
54
  id: getTrad('Settings.roles.created'),
63
55
  defaultMessage: 'Role created',
64
- },
56
+ }),
65
57
  });
66
58
 
67
59
  // Forcing redirecting since we don't have the id in the response
68
- goBack();
60
+ navigate(-1);
69
61
  },
70
62
  });
71
63
 
72
64
  const handleCreateRoleSubmit = async (data) => {
73
- lockApp();
74
-
75
65
  // TODO: refactor. Child -> parent component communication is evil;
76
66
  // We should either move the provider one level up or move the state
77
67
  // straight into redux.
78
68
  const permissions = permissionsRef.current.getPermissions();
79
69
 
80
70
  await mutation.mutate({ ...data, ...permissions, users: [] });
81
-
82
- unlockApp();
83
71
  };
84
72
 
85
73
  return (
86
74
  <Main>
87
- {/* TODO: This needs to be translated */}
88
- <SettingsPageTitle name="Roles" />
75
+ <Helmet
76
+ title={formatMessage(
77
+ { id: 'Settings.PageTitle', defaultMessage: 'Settings - {name}' },
78
+ { name: 'Roles' }
79
+ )}
80
+ />
89
81
  <Formik
90
82
  enableReinitialize
91
83
  initialValues={{ name: '', description: '' }}
@@ -193,7 +185,7 @@ export const CreatePage = () => {
193
185
  };
194
186
 
195
187
  export const ProtectedRolesCreatePage = () => (
196
- <CheckPagePermissions permissions={PERMISSIONS.createRole}>
188
+ <Page.Protect permissions={PERMISSIONS.createRole}>
197
189
  <CreatePage />
198
- </CheckPagePermissions>
190
+ </Page.Protect>
199
191
  );
@@ -12,22 +12,19 @@ import {
12
12
  GridItem,
13
13
  Grid,
14
14
  } from '@strapi/design-system';
15
+ import { Check } from '@strapi/icons';
15
16
  import {
16
- CheckPagePermissions,
17
- useOverlayBlocker,
18
- SettingsPageTitle,
19
- LoadingIndicatorPage,
20
- Form,
17
+ Page,
18
+ BackButton,
21
19
  useAPIErrorHandler,
22
- useFetchClient,
23
20
  useNotification,
24
- Link,
25
- } from '@strapi/helper-plugin';
26
- import { ArrowLeft, Check } from '@strapi/icons';
27
- import { Formik } from 'formik';
21
+ useFetchClient,
22
+ } from '@strapi/strapi/admin';
23
+ import { Formik, Form } from 'formik';
24
+ import { Helmet } from 'react-helmet';
28
25
  import { useIntl } from 'react-intl';
29
26
  import { useQuery, useMutation } from 'react-query';
30
- import { useRouteMatch } from 'react-router-dom';
27
+ import { useMatch } from 'react-router-dom';
31
28
 
32
29
  import UsersPermissions from '../../../components/UsersPermissions';
33
30
  import { PERMISSIONS } from '../../../constants';
@@ -37,11 +34,10 @@ import { usePlugins } from '../hooks/usePlugins';
37
34
 
38
35
  export const EditPage = () => {
39
36
  const { formatMessage } = useIntl();
40
- const toggleNotification = useNotification();
41
- const { lockApp, unlockApp } = useOverlayBlocker();
37
+ const { toggleNotification } = useNotification();
42
38
  const {
43
39
  params: { id },
44
- } = useRouteMatch(`/settings/users-permissions/roles/:id`);
40
+ } = useMatch(`/settings/users-permissions/roles/:id`);
45
41
  const { get } = useFetchClient();
46
42
  const { isLoading: isLoadingPlugins, routes } = usePlugins();
47
43
  const {
@@ -63,7 +59,7 @@ export const EditPage = () => {
63
59
  const mutation = useMutation((body) => put(`/users-permissions/roles/${id}`, body), {
64
60
  onError(error) {
65
61
  toggleNotification({
66
- type: 'warning',
62
+ type: 'danger',
67
63
  message: formatAPIError(error),
68
64
  });
69
65
  },
@@ -71,10 +67,10 @@ export const EditPage = () => {
71
67
  async onSuccess() {
72
68
  toggleNotification({
73
69
  type: 'success',
74
- message: {
70
+ message: formatMessage({
75
71
  id: getTrad('Settings.roles.created'),
76
72
  defaultMessage: 'Role edited',
77
- },
73
+ }),
78
74
  });
79
75
 
80
76
  await refetchRole();
@@ -82,24 +78,23 @@ export const EditPage = () => {
82
78
  });
83
79
 
84
80
  const handleEditRoleSubmit = async (data) => {
85
- // Set loading state
86
- lockApp();
87
-
88
81
  const permissions = permissionsRef.current.getPermissions();
89
82
 
90
83
  await mutation.mutate({ ...data, ...permissions, users: [] });
91
-
92
- unlockApp();
93
84
  };
94
85
 
95
86
  if (isLoadingRole) {
96
- return <LoadingIndicatorPage />;
87
+ return <Page.Loading />;
97
88
  }
98
89
 
99
90
  return (
100
91
  <Main>
101
- {/* TODO: this needs to be translated */}
102
- <SettingsPageTitle name="Roles" />
92
+ <Helmet
93
+ title={formatMessage(
94
+ { id: 'Settings.PageTitle', defaultMessage: 'Settings - {name}' },
95
+ { name: 'Roles' }
96
+ )}
97
+ />
103
98
  <Formik
104
99
  enableReinitialize
105
100
  initialValues={{ name: role.name, description: role.description }}
@@ -110,7 +105,7 @@ export const EditPage = () => {
110
105
  <Form noValidate onSubmit={handleSubmit}>
111
106
  <HeaderLayout
112
107
  primaryAction={
113
- !isLoadingPlugins && (
108
+ !isLoadingPlugins ? (
114
109
  <Button
115
110
  disabled={role.code === 'strapi-super-admin'}
116
111
  type="submit"
@@ -122,18 +117,11 @@ export const EditPage = () => {
122
117
  defaultMessage: 'Save',
123
118
  })}
124
119
  </Button>
125
- )
120
+ ) : null
126
121
  }
127
122
  title={role.name}
128
123
  subtitle={role.description}
129
- navigationAction={
130
- <Link startIcon={<ArrowLeft />} to="/settings/users-permissions/roles">
131
- {formatMessage({
132
- id: 'global.back',
133
- defaultMessage: 'Back',
134
- })}
135
- </Link>
136
- }
124
+ navigationAction={<BackButton />}
137
125
  />
138
126
  <ContentLayout>
139
127
  <Flex
@@ -214,7 +202,7 @@ export const EditPage = () => {
214
202
  };
215
203
 
216
204
  export const ProtectedRolesEditPage = () => (
217
- <CheckPagePermissions permissions={PERMISSIONS.updateRole}>
205
+ <Page.Protect permissions={PERMISSIONS.updateRole}>
218
206
  <EditPage />
219
- </CheckPagePermissions>
207
+ </Page.Protect>
220
208
  );
@@ -1,24 +1,23 @@
1
1
  import React from 'react';
2
2
 
3
3
  import { Flex, IconButton, Link, Tbody, Td, Tr, Typography } from '@strapi/design-system';
4
- import { CheckPermissions, onRowClick, pxToRem, stopPropagation } from '@strapi/helper-plugin';
5
4
  import { Pencil, Trash } from '@strapi/icons';
6
5
  import PropTypes from 'prop-types';
7
6
  import { useIntl } from 'react-intl';
8
- import { useHistory } from 'react-router-dom';
7
+ import { useNavigate } from 'react-router-dom';
9
8
  import styled from 'styled-components';
10
9
 
11
10
  const EditLink = styled(Link)`
12
11
  align-items: center;
13
- height: ${pxToRem(32)};
12
+ height: ${32 / 16}rem;
14
13
  display: flex;
15
14
  justify-content: center;
16
15
  padding: ${({ theme }) => `${theme.spaces[2]}}`};
17
- width: ${pxToRem(32)};
16
+ width: ${32 / 16}rem;
18
17
 
19
18
  svg {
20
- height: ${pxToRem(12)};
21
- width: ${pxToRem(12)};
19
+ height: ${12 / 16}rem;
20
+ width: ${12 / 16}rem;
22
21
 
23
22
  path {
24
23
  fill: ${({ theme }) => theme.colors.neutral500};
@@ -35,9 +34,9 @@ const EditLink = styled(Link)`
35
34
  }
36
35
  `;
37
36
 
38
- const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDelete }) => {
37
+ const TableBody = ({ sortedRoles, canDelete, canUpdate, setRoleToDelete, onDelete }) => {
39
38
  const { formatMessage } = useIntl();
40
- const { push } = useHistory();
39
+ const navigate = useNavigate();
41
40
  const [showConfirmDelete, setShowConfirmDelete] = onDelete;
42
41
 
43
42
  const checkCanDeleteRole = (role) =>
@@ -48,14 +47,10 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
48
47
  setShowConfirmDelete(!showConfirmDelete);
49
48
  };
50
49
 
51
- const handleClickEdit = (id) => {
52
- push(`/settings/users-permissions/roles/${id}`);
53
- };
54
-
55
50
  return (
56
51
  <Tbody>
57
52
  {sortedRoles?.map((role) => (
58
- <Tr key={role.name} {...onRowClick({ fn: () => handleClickEdit(role.id) })}>
53
+ <Tr key={role.name} onClick={() => navigate(role.id.toString())}>
59
54
  <Td width="20%">
60
55
  <Typography>{role.name}</Typography>
61
56
  </Td>
@@ -74,10 +69,10 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
74
69
  </Typography>
75
70
  </Td>
76
71
  <Td>
77
- <Flex justifyContent="end" {...stopPropagation}>
78
- <CheckPermissions permissions={permissions.updateRole}>
72
+ <Flex justifyContent="end" onClick={(e) => e.stopPropagation()}>
73
+ {canUpdate ? (
79
74
  <EditLink
80
- to={`/settings/users-permissions/roles/${role.id}`}
75
+ to={role.id.toString()}
81
76
  aria-label={formatMessage(
82
77
  { id: 'app.component.table.edit', defaultMessage: 'Edit {target}' },
83
78
  { target: `${role.name}` }
@@ -85,20 +80,18 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
85
80
  >
86
81
  <Pencil />
87
82
  </EditLink>
88
- </CheckPermissions>
83
+ ) : null}
89
84
 
90
85
  {checkCanDeleteRole(role) && (
91
- <CheckPermissions permissions={permissions.deleteRole}>
92
- <IconButton
93
- onClick={() => handleClickDelete(role.id)}
94
- noBorder
95
- icon={<Trash />}
96
- label={formatMessage(
97
- { id: 'global.delete-target', defaultMessage: 'Delete {target}' },
98
- { target: `${role.name}` }
99
- )}
100
- />
101
- </CheckPermissions>
86
+ <IconButton
87
+ onClick={() => handleClickDelete(role.id.toString())}
88
+ noBorder
89
+ icon={<Trash />}
90
+ label={formatMessage(
91
+ { id: 'global.delete-target', defaultMessage: 'Delete {target}' },
92
+ { target: `${role.name}` }
93
+ )}
94
+ />
102
95
  )}
103
96
  </Flex>
104
97
  </Td>
@@ -112,6 +105,7 @@ export default TableBody;
112
105
 
113
106
  TableBody.defaultProps = {
114
107
  canDelete: false,
108
+ canUpdate: false,
115
109
  };
116
110
 
117
111
  TableBody.propTypes = {
@@ -120,4 +114,5 @@ TableBody.propTypes = {
120
114
  setRoleToDelete: PropTypes.func.isRequired,
121
115
  sortedRoles: PropTypes.array.isRequired,
122
116
  canDelete: PropTypes.bool,
117
+ canUpdate: PropTypes.bool,
123
118
  };
@@ -5,7 +5,6 @@ import {
5
5
  ContentLayout,
6
6
  HeaderLayout,
7
7
  Layout,
8
- Main,
9
8
  Table,
10
9
  Th,
11
10
  Thead,
@@ -13,50 +12,47 @@ import {
13
12
  Typography,
14
13
  useNotifyAT,
15
14
  VisuallyHidden,
16
- } from '@strapi/design-system';
17
- import {
18
- CheckPagePermissions,
19
- CheckPermissions,
20
- ConfirmDialog,
21
15
  EmptyStateLayout,
22
- LinkButton,
23
- LoadingIndicatorPage,
24
- NoPermissions,
25
- SearchURLQuery,
26
- SettingsPageTitle,
27
16
  useCollator,
28
17
  useFilter,
29
- useFocusWhenNavigate,
18
+ } from '@strapi/design-system';
19
+ import { LinkButton } from '@strapi/design-system/v2';
20
+ import { useRBAC } from '@strapi/helper-plugin';
21
+ import { Plus } from '@strapi/icons';
22
+ import {
23
+ ConfirmDialog,
24
+ useTracking,
25
+ Page,
26
+ SearchInput,
27
+ BackButton,
30
28
  useNotification,
31
29
  useQueryParams,
32
- useRBAC,
33
- useTracking,
34
- } from '@strapi/helper-plugin';
35
- import { Plus } from '@strapi/icons';
30
+ useFetchClient,
31
+ } from '@strapi/strapi/admin';
32
+ import { Helmet } from 'react-helmet';
36
33
  import { useIntl } from 'react-intl';
37
34
  import { useMutation, useQuery } from 'react-query';
35
+ import { NavLink } from 'react-router-dom';
38
36
 
39
37
  import { PERMISSIONS } from '../../../../constants';
40
38
  import { getTrad } from '../../../../utils';
41
39
 
42
40
  import TableBody from './components/TableBody';
43
- import { deleteData, fetchData } from './utils/api';
44
41
 
45
42
  export const RolesListPage = () => {
46
43
  const { trackUsage } = useTracking();
47
44
  const { formatMessage, locale } = useIntl();
48
- const toggleNotification = useNotification();
45
+ const { toggleNotification } = useNotification();
49
46
  const { notifyStatus } = useNotifyAT();
50
47
  const [{ query }] = useQueryParams();
51
48
  const _q = query?._q || '';
52
49
  const [showConfirmDelete, setShowConfirmDelete] = useState(false);
53
- const [isConfirmButtonLoading, setIsConfirmButtonLoading] = useState(false);
54
50
  const [roleToDelete, setRoleToDelete] = useState();
55
- useFocusWhenNavigate();
51
+ const { del, get } = useFetchClient();
56
52
 
57
53
  const {
58
54
  isLoading: isLoadingForPermissions,
59
- allowedActions: { canRead, canDelete },
55
+ allowedActions: { canRead, canDelete, canCreate, canUpdate },
60
56
  } = useRBAC({
61
57
  create: PERMISSIONS.createRole,
62
58
  read: PERMISSIONS.readRoles,
@@ -69,12 +65,12 @@ export const RolesListPage = () => {
69
65
  data: { roles },
70
66
  isFetching,
71
67
  refetch,
72
- } = useQuery('get-roles', () => fetchData(toggleNotification, notifyStatus), {
68
+ } = useQuery('get-roles', () => fetchData(toggleNotification, formatMessage, notifyStatus), {
73
69
  initialData: {},
74
70
  enabled: canRead,
75
71
  });
76
72
 
77
- const { includes } = useFilter(locale, {
73
+ const { contains } = useFilter(locale, {
78
74
  sensitivity: 'base',
79
75
  });
80
76
 
@@ -85,12 +81,39 @@ export const RolesListPage = () => {
85
81
  sensitivity: 'base',
86
82
  });
87
83
 
88
- const isLoading = isLoadingForData || isFetching;
84
+ const isLoading = isLoadingForData || isFetching || isLoadingForPermissions;
89
85
 
90
86
  const handleShowConfirmDelete = () => {
91
87
  setShowConfirmDelete(!showConfirmDelete);
92
88
  };
93
89
 
90
+ const deleteData = async (id, formatMessage, toggleNotification) => {
91
+ try {
92
+ await del(`/users-permissions/roles/${id}`);
93
+ } catch (error) {
94
+ toggleNotification({
95
+ type: 'danger',
96
+ message: formatMessage({ id: 'notification.error', defaultMessage: 'An error occured' }),
97
+ });
98
+ }
99
+ };
100
+
101
+ const fetchData = async (toggleNotification, formatMessage, notifyStatus) => {
102
+ try {
103
+ const { data } = await get('/users-permissions/roles');
104
+ notifyStatus('The roles have loaded successfully');
105
+
106
+ return data;
107
+ } catch (err) {
108
+ toggleNotification({
109
+ type: 'danger',
110
+ message: formatMessage({ id: 'notification.error' }),
111
+ });
112
+
113
+ throw new Error(err);
114
+ }
115
+ };
116
+
94
117
  const emptyLayout = {
95
118
  roles: {
96
119
  id: getTrad('Roles.empty'),
@@ -107,21 +130,19 @@ export const RolesListPage = () => {
107
130
  defaultMessage: 'Roles',
108
131
  });
109
132
 
110
- const deleteMutation = useMutation((id) => deleteData(id, toggleNotification), {
133
+ const deleteMutation = useMutation((id) => deleteData(id, formatMessage, toggleNotification), {
111
134
  async onSuccess() {
112
135
  await refetch();
113
136
  },
114
137
  });
115
138
 
116
139
  const handleConfirmDelete = async () => {
117
- setIsConfirmButtonLoading(true);
118
140
  await deleteMutation.mutateAsync(roleToDelete);
119
141
  setShowConfirmDelete(!showConfirmDelete);
120
- setIsConfirmButtonLoading(false);
121
142
  };
122
143
 
123
144
  const sortedRoles = (roles || [])
124
- .filter((role) => includes(role.name, _q) || includes(role.description, _q))
145
+ .filter((role) => contains(role.name, _q) || contains(role.description, _q))
125
146
  .sort(
126
147
  (a, b) => formatter.compare(a.name, b.name) || formatter.compare(a.description, b.description)
127
148
  );
@@ -131,10 +152,19 @@ export const RolesListPage = () => {
131
152
  const colCount = 4;
132
153
  const rowCount = (roles?.length || 0) + 1;
133
154
 
155
+ if (isLoading) {
156
+ return <Page.Loading />;
157
+ }
158
+
134
159
  return (
135
160
  <Layout>
136
- <SettingsPageTitle name={pageTitle} />
137
- <Main aria-busy={isLoading}>
161
+ <Helmet
162
+ title={formatMessage(
163
+ { id: 'Settings.PageTitle', defaultMessage: 'Settings - {name}' },
164
+ { name: pageTitle }
165
+ )}
166
+ />
167
+ <Page.Main>
138
168
  <HeaderLayout
139
169
  title={formatMessage({
140
170
  id: 'global.roles',
@@ -145,9 +175,10 @@ export const RolesListPage = () => {
145
175
  defaultMessage: 'List of roles',
146
176
  })}
147
177
  primaryAction={
148
- <CheckPermissions permissions={PERMISSIONS.createRole}>
178
+ canCreate ? (
149
179
  <LinkButton
150
- to="/settings/users-permissions/roles/new"
180
+ to="new"
181
+ as={NavLink}
151
182
  onClick={() => trackUsage('willCreateRole')}
152
183
  startIcon={<Plus />}
153
184
  size="S"
@@ -157,13 +188,14 @@ export const RolesListPage = () => {
157
188
  defaultMessage: 'Add new role',
158
189
  })}
159
190
  </LinkButton>
160
- </CheckPermissions>
191
+ ) : null
161
192
  }
193
+ navigationAction={<BackButton />}
162
194
  />
163
195
 
164
196
  <ActionLayout
165
197
  startActions={
166
- <SearchURLQuery
198
+ <SearchInput
167
199
  label={formatMessage({
168
200
  id: 'app.component.search.label',
169
201
  defaultMessage: 'Search',
@@ -173,8 +205,7 @@ export const RolesListPage = () => {
173
205
  />
174
206
 
175
207
  <ContentLayout>
176
- {!canRead && <NoPermissions />}
177
- {(isLoading || isLoadingForPermissions) && <LoadingIndicatorPage />}
208
+ {!canRead && <Page.NoPermissions />}
178
209
  {canRead && sortedRoles && sortedRoles?.length ? (
179
210
  <Table colCount={colCount} rowCount={rowCount}>
180
211
  <Thead>
@@ -213,30 +244,30 @@ export const RolesListPage = () => {
213
244
  <TableBody
214
245
  sortedRoles={sortedRoles}
215
246
  canDelete={canDelete}
247
+ canUpdate={canUpdate}
216
248
  permissions={PERMISSIONS}
217
249
  setRoleToDelete={setRoleToDelete}
218
250
  onDelete={[showConfirmDelete, setShowConfirmDelete]}
219
251
  />
220
252
  </Table>
221
253
  ) : (
222
- <EmptyStateLayout content={emptyLayout[emptyContent]} />
254
+ <EmptyStateLayout content={formatMessage(emptyLayout[emptyContent])} />
223
255
  )}
224
256
  </ContentLayout>
225
257
  <ConfirmDialog
226
- isConfirmButtonLoading={isConfirmButtonLoading}
227
258
  onConfirm={handleConfirmDelete}
228
- onToggleDialog={handleShowConfirmDelete}
259
+ onClose={handleShowConfirmDelete}
229
260
  isOpen={showConfirmDelete}
230
261
  />
231
- </Main>
262
+ </Page.Main>
232
263
  </Layout>
233
264
  );
234
265
  };
235
266
 
236
267
  export const ProtectedRolesListPage = () => {
237
268
  return (
238
- <CheckPagePermissions permissions={PERMISSIONS.accessRoles}>
269
+ <Page.Protect permissions={PERMISSIONS.accessRoles}>
239
270
  <RolesListPage />
240
- </CheckPagePermissions>
271
+ </Page.Protect>
241
272
  );
242
273
  };
@@ -60,7 +60,7 @@
60
60
  "Settings.roles.deleted": "Role deleted",
61
61
  "Settings.roles.edited": "Role edited",
62
62
  "Settings.section-label": "Users & Permissions plugin",
63
- "components.Input.error.validation.email": "This is an invalid email",
63
+ "components.Input.error.validation.email": "This is not a valid email",
64
64
  "components.Input.error.validation.json": "This doesn't match the JSON format",
65
65
  "components.Input.error.validation.max": "The value is too high.",
66
66
  "components.Input.error.validation.maxLength": "The value is too long.",
@@ -0,0 +1,13 @@
1
+ const prefixPluginTranslations = (trad, pluginId) => {
2
+ if (!pluginId) {
3
+ throw new TypeError("pluginId can't be empty");
4
+ }
5
+
6
+ return Object.keys(trad).reduce((acc, current) => {
7
+ acc[`${pluginId}.${current}`] = trad[current];
8
+
9
+ return acc;
10
+ }, {});
11
+ };
12
+
13
+ export { prefixPluginTranslations };