@strapi/plugin-users-permissions 4.20.4 → 5.0.0-alpha.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 (108) 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 +14 -30
  5. package/admin/src/pages/AdvancedSettings/index.jsx +69 -107
  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/index.jsx +22 -50
  10. package/admin/src/pages/EmailTemplates/utils/schema.js +18 -6
  11. package/admin/src/pages/Providers/index.jsx +91 -96
  12. package/admin/src/pages/Providers/utils/forms.js +11 -11
  13. package/admin/src/pages/Roles/constants.js +3 -3
  14. package/admin/src/pages/Roles/hooks/usePlugins.js +5 -4
  15. package/admin/src/pages/Roles/index.jsx +9 -18
  16. package/admin/src/pages/Roles/pages/CreatePage.jsx +21 -28
  17. package/admin/src/pages/Roles/pages/EditPage.jsx +23 -40
  18. package/admin/src/pages/Roles/pages/ListPage/components/TableBody.jsx +23 -27
  19. package/admin/src/pages/Roles/pages/ListPage/index.jsx +42 -38
  20. package/admin/src/pages/Roles/pages/ListPage/utils/api.js +6 -6
  21. package/admin/src/translations/en.json +1 -1
  22. package/dist/_chunks/EditViewPage-xYzUSAwS-5mOQ_-nB.mjs +84370 -0
  23. package/dist/_chunks/EditViewPage-xYzUSAwS-5mOQ_-nB.mjs.map +1 -0
  24. package/dist/_chunks/EditViewPage-xYzUSAwS-wpHlxdkC.js +84398 -0
  25. package/dist/_chunks/EditViewPage-xYzUSAwS-wpHlxdkC.js.map +1 -0
  26. package/dist/_chunks/Helmet-d9JljxUo.js +1010 -0
  27. package/dist/_chunks/Helmet-d9JljxUo.js.map +1 -0
  28. package/dist/_chunks/Helmet-kyJ1Zklj.mjs +1008 -0
  29. package/dist/_chunks/Helmet-kyJ1Zklj.mjs.map +1 -0
  30. package/dist/_chunks/ListViewPage-xOVa04T_-elahT0e9.js +1618 -0
  31. package/dist/_chunks/ListViewPage-xOVa04T_-elahT0e9.js.map +1 -0
  32. package/dist/_chunks/ListViewPage-xOVa04T_-lbfb219V.mjs +1595 -0
  33. package/dist/_chunks/ListViewPage-xOVa04T_-lbfb219V.mjs.map +1 -0
  34. package/dist/_chunks/ReviewWorkflowsColumn-FDxVKdto-IWfB3WVH.mjs +33 -0
  35. package/dist/_chunks/ReviewWorkflowsColumn-FDxVKdto-IWfB3WVH.mjs.map +1 -0
  36. package/dist/_chunks/ReviewWorkflowsColumn-FDxVKdto-m8hslkeI.js +33 -0
  37. package/dist/_chunks/ReviewWorkflowsColumn-FDxVKdto-m8hslkeI.js.map +1 -0
  38. package/dist/_chunks/constants-WjN6I3sL-7e3gpx4b.mjs +190 -0
  39. package/dist/_chunks/constants-WjN6I3sL-7e3gpx4b.mjs.map +1 -0
  40. package/dist/_chunks/constants-WjN6I3sL-cDZPE6ED.js +209 -0
  41. package/dist/_chunks/constants-WjN6I3sL-cDZPE6ED.js.map +1 -0
  42. package/dist/_chunks/{en-m608rMZx.js → en-TaNIVnDO.js} +2 -2
  43. package/dist/_chunks/en-TaNIVnDO.js.map +1 -0
  44. package/dist/_chunks/{en-CE3wEy_c.mjs → en-jvJ-d-Qq.mjs} +2 -2
  45. package/dist/_chunks/en-jvJ-d-Qq.mjs.map +1 -0
  46. package/dist/_chunks/{index-BWyhWRPa.mjs → index-53jX2hhF.mjs} +28 -36
  47. package/dist/_chunks/index-53jX2hhF.mjs.map +1 -0
  48. package/dist/_chunks/index-5ZvCaCyY-06DLg5eU.mjs +16421 -0
  49. package/dist/_chunks/index-5ZvCaCyY-06DLg5eU.mjs.map +1 -0
  50. package/dist/_chunks/index-5ZvCaCyY-JgYo3Jws.js +16446 -0
  51. package/dist/_chunks/index-5ZvCaCyY-JgYo3Jws.js.map +1 -0
  52. package/dist/_chunks/{index-kKUYoIsq.mjs → index-A3oJlPcE.mjs} +111 -108
  53. package/dist/_chunks/index-A3oJlPcE.mjs.map +1 -0
  54. package/dist/_chunks/{index-MfP0ffb0.js → index-MEUac_4V.js} +124 -122
  55. package/dist/_chunks/index-MEUac_4V.js.map +1 -0
  56. package/dist/_chunks/index-N-GcFWtg.mjs +261 -0
  57. package/dist/_chunks/index-N-GcFWtg.mjs.map +1 -0
  58. package/dist/_chunks/{index-tUo88Kqt.js → index-TJUxOCtJ.js} +28 -36
  59. package/dist/_chunks/index-TJUxOCtJ.js.map +1 -0
  60. package/dist/_chunks/{index-sarofNNK.js → index-VgvlwVA7.js} +106 -147
  61. package/dist/_chunks/index-VgvlwVA7.js.map +1 -0
  62. package/dist/_chunks/{index-KmMoN4sr.js → index-bRuKnVcH.js} +39 -32
  63. package/dist/_chunks/index-bRuKnVcH.js.map +1 -0
  64. package/dist/_chunks/{index-FETf_nGC.mjs → index-ee_14Ldw.mjs} +36 -29
  65. package/dist/_chunks/index-ee_14Ldw.mjs.map +1 -0
  66. package/dist/_chunks/index-mzJ2Vb5u.js +280 -0
  67. package/dist/_chunks/index-mzJ2Vb5u.js.map +1 -0
  68. package/dist/_chunks/{index-xHL-7Hse.mjs → index-vXywiVeM.mjs} +101 -142
  69. package/dist/_chunks/index-vXywiVeM.mjs.map +1 -0
  70. package/dist/_chunks/useSyncRbac-Kt8Li0Yf-Z54sMEPM.mjs +39 -0
  71. package/dist/_chunks/useSyncRbac-Kt8Li0Yf-Z54sMEPM.mjs.map +1 -0
  72. package/dist/_chunks/useSyncRbac-Kt8Li0Yf-h8HqtZ6y.js +57 -0
  73. package/dist/_chunks/useSyncRbac-Kt8Li0Yf-h8HqtZ6y.js.map +1 -0
  74. package/dist/admin/index.js +1 -1
  75. package/dist/admin/index.mjs +1 -1
  76. package/dist/style.css +84 -0
  77. package/package.json +15 -15
  78. package/server/bootstrap/grant-config.js +9 -0
  79. package/server/bootstrap/index.js +2 -39
  80. package/server/content-types/user/index.js +0 -1
  81. package/server/controllers/auth.js +24 -53
  82. package/server/controllers/content-manager-user.js +24 -28
  83. package/server/controllers/role.js +1 -1
  84. package/server/controllers/user.js +5 -5
  85. package/server/middlewares/rateLimit.js +1 -1
  86. package/server/register.js +1 -1
  87. package/server/services/jwt.js +3 -3
  88. package/server/services/permission.js +3 -7
  89. package/server/services/providers-registry.js +15 -0
  90. package/server/services/providers.js +10 -5
  91. package/server/services/role.js +15 -13
  92. package/server/services/user.js +28 -14
  93. package/server/services/users-permissions.js +12 -10
  94. package/server/utils/sanitize/sanitizers.js +2 -2
  95. package/dist/_chunks/en-CE3wEy_c.mjs.map +0 -1
  96. package/dist/_chunks/en-m608rMZx.js.map +0 -1
  97. package/dist/_chunks/index-4W1jrPVd.js +0 -320
  98. package/dist/_chunks/index-4W1jrPVd.js.map +0 -1
  99. package/dist/_chunks/index-BWyhWRPa.mjs.map +0 -1
  100. package/dist/_chunks/index-FETf_nGC.mjs.map +0 -1
  101. package/dist/_chunks/index-H0k1w1px.mjs +0 -301
  102. package/dist/_chunks/index-H0k1w1px.mjs.map +0 -1
  103. package/dist/_chunks/index-KmMoN4sr.js.map +0 -1
  104. package/dist/_chunks/index-MfP0ffb0.js.map +0 -1
  105. package/dist/_chunks/index-kKUYoIsq.mjs.map +0 -1
  106. package/dist/_chunks/index-sarofNNK.js.map +0 -1
  107. package/dist/_chunks/index-tUo88Kqt.js.map +0 -1
  108. package/dist/_chunks/index-xHL-7Hse.mjs.map +0 -1
@@ -12,20 +12,14 @@ 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';
15
+ import { useFetchClient } from '@strapi/helper-plugin';
24
16
  import { Check } from '@strapi/icons';
25
- import { Formik } from 'formik';
17
+ import { Page, useTracking, useNotification } from '@strapi/strapi/admin';
18
+ import { Formik, Form } from 'formik';
19
+ import { Helmet } from 'react-helmet';
26
20
  import { useIntl } from 'react-intl';
27
21
  import { useMutation } from 'react-query';
28
- import { useHistory } from 'react-router-dom';
22
+ import { useNavigate } from 'react-router-dom';
29
23
 
30
24
  import UsersPermissions from '../../../components/UsersPermissions';
31
25
  import { PERMISSIONS } from '../../../constants';
@@ -35,9 +29,8 @@ import { usePlugins } from '../hooks/usePlugins';
35
29
 
36
30
  export const CreatePage = () => {
37
31
  const { formatMessage } = useIntl();
38
- const toggleNotification = useNotification();
39
- const { goBack } = useHistory();
40
- const { lockApp, unlockApp } = useOverlayBlocker();
32
+ const { toggleNotification } = useNotification();
33
+ const navigate = useNavigate();
41
34
  const { isLoading: isLoadingPlugins, permissions, routes } = usePlugins();
42
35
  const { trackUsage } = useTracking();
43
36
  const permissionsRef = React.useRef();
@@ -45,11 +38,11 @@ export const CreatePage = () => {
45
38
  const mutation = useMutation((body) => post(`/users-permissions/roles`, body), {
46
39
  onError() {
47
40
  toggleNotification({
48
- type: 'warning',
49
- message: {
41
+ type: 'danger',
42
+ message: formatMessage({
50
43
  id: 'notification.error',
51
44
  defaultMessage: 'An error occurred',
52
- },
45
+ }),
53
46
  });
54
47
  },
55
48
 
@@ -58,34 +51,34 @@ export const CreatePage = () => {
58
51
 
59
52
  toggleNotification({
60
53
  type: 'success',
61
- message: {
54
+ message: formatMessage({
62
55
  id: getTrad('Settings.roles.created'),
63
56
  defaultMessage: 'Role created',
64
- },
57
+ }),
65
58
  });
66
59
 
67
60
  // Forcing redirecting since we don't have the id in the response
68
- goBack();
61
+ navigate(-1);
69
62
  },
70
63
  });
71
64
 
72
65
  const handleCreateRoleSubmit = async (data) => {
73
- lockApp();
74
-
75
66
  // TODO: refactor. Child -> parent component communication is evil;
76
67
  // We should either move the provider one level up or move the state
77
68
  // straight into redux.
78
69
  const permissions = permissionsRef.current.getPermissions();
79
70
 
80
71
  await mutation.mutate({ ...data, ...permissions, users: [] });
81
-
82
- unlockApp();
83
72
  };
84
73
 
85
74
  return (
86
75
  <Main>
87
- {/* TODO: This needs to be translated */}
88
- <SettingsPageTitle name="Roles" />
76
+ <Helmet
77
+ title={formatMessage(
78
+ { id: 'Settings.PageTitle', defaultMessage: 'Settings - {name}' },
79
+ { name: 'Roles' }
80
+ )}
81
+ />
89
82
  <Formik
90
83
  enableReinitialize
91
84
  initialValues={{ name: '', description: '' }}
@@ -193,7 +186,7 @@ export const CreatePage = () => {
193
186
  };
194
187
 
195
188
  export const ProtectedRolesCreatePage = () => (
196
- <CheckPagePermissions permissions={PERMISSIONS.createRole}>
189
+ <Page.Protect permissions={PERMISSIONS.createRole}>
197
190
  <CreatePage />
198
- </CheckPagePermissions>
191
+ </Page.Protect>
199
192
  );
@@ -12,22 +12,14 @@ import {
12
12
  GridItem,
13
13
  Grid,
14
14
  } from '@strapi/design-system';
15
- import {
16
- CheckPagePermissions,
17
- useOverlayBlocker,
18
- SettingsPageTitle,
19
- LoadingIndicatorPage,
20
- Form,
21
- useAPIErrorHandler,
22
- useFetchClient,
23
- useNotification,
24
- Link,
25
- } from '@strapi/helper-plugin';
26
- import { ArrowLeft, Check } from '@strapi/icons';
27
- import { Formik } from 'formik';
15
+ import { useFetchClient } from '@strapi/helper-plugin';
16
+ import { Check } from '@strapi/icons';
17
+ import { Page, BackButton, useAPIErrorHandler, useNotification } from '@strapi/strapi/admin';
18
+ import { Formik, Form } from 'formik';
19
+ import { Helmet } from 'react-helmet';
28
20
  import { useIntl } from 'react-intl';
29
21
  import { useQuery, useMutation } from 'react-query';
30
- import { useRouteMatch } from 'react-router-dom';
22
+ import { useMatch } from 'react-router-dom';
31
23
 
32
24
  import UsersPermissions from '../../../components/UsersPermissions';
33
25
  import { PERMISSIONS } from '../../../constants';
@@ -37,11 +29,10 @@ import { usePlugins } from '../hooks/usePlugins';
37
29
 
38
30
  export const EditPage = () => {
39
31
  const { formatMessage } = useIntl();
40
- const toggleNotification = useNotification();
41
- const { lockApp, unlockApp } = useOverlayBlocker();
32
+ const { toggleNotification } = useNotification();
42
33
  const {
43
34
  params: { id },
44
- } = useRouteMatch(`/settings/users-permissions/roles/:id`);
35
+ } = useMatch(`/settings/users-permissions/roles/:id`);
45
36
  const { get } = useFetchClient();
46
37
  const { isLoading: isLoadingPlugins, routes } = usePlugins();
47
38
  const {
@@ -63,7 +54,7 @@ export const EditPage = () => {
63
54
  const mutation = useMutation((body) => put(`/users-permissions/roles/${id}`, body), {
64
55
  onError(error) {
65
56
  toggleNotification({
66
- type: 'warning',
57
+ type: 'danger',
67
58
  message: formatAPIError(error),
68
59
  });
69
60
  },
@@ -71,10 +62,10 @@ export const EditPage = () => {
71
62
  async onSuccess() {
72
63
  toggleNotification({
73
64
  type: 'success',
74
- message: {
65
+ message: formatMessage({
75
66
  id: getTrad('Settings.roles.created'),
76
67
  defaultMessage: 'Role edited',
77
- },
68
+ }),
78
69
  });
79
70
 
80
71
  await refetchRole();
@@ -82,24 +73,23 @@ export const EditPage = () => {
82
73
  });
83
74
 
84
75
  const handleEditRoleSubmit = async (data) => {
85
- // Set loading state
86
- lockApp();
87
-
88
76
  const permissions = permissionsRef.current.getPermissions();
89
77
 
90
78
  await mutation.mutate({ ...data, ...permissions, users: [] });
91
-
92
- unlockApp();
93
79
  };
94
80
 
95
81
  if (isLoadingRole) {
96
- return <LoadingIndicatorPage />;
82
+ return <Page.Loading />;
97
83
  }
98
84
 
99
85
  return (
100
86
  <Main>
101
- {/* TODO: this needs to be translated */}
102
- <SettingsPageTitle name="Roles" />
87
+ <Helmet
88
+ title={formatMessage(
89
+ { id: 'Settings.PageTitle', defaultMessage: 'Settings - {name}' },
90
+ { name: 'Roles' }
91
+ )}
92
+ />
103
93
  <Formik
104
94
  enableReinitialize
105
95
  initialValues={{ name: role.name, description: role.description }}
@@ -110,7 +100,7 @@ export const EditPage = () => {
110
100
  <Form noValidate onSubmit={handleSubmit}>
111
101
  <HeaderLayout
112
102
  primaryAction={
113
- !isLoadingPlugins && (
103
+ !isLoadingPlugins ? (
114
104
  <Button
115
105
  disabled={role.code === 'strapi-super-admin'}
116
106
  type="submit"
@@ -122,18 +112,11 @@ export const EditPage = () => {
122
112
  defaultMessage: 'Save',
123
113
  })}
124
114
  </Button>
125
- )
115
+ ) : null
126
116
  }
127
117
  title={role.name}
128
118
  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
- }
119
+ navigationAction={<BackButton />}
137
120
  />
138
121
  <ContentLayout>
139
122
  <Flex
@@ -214,7 +197,7 @@ export const EditPage = () => {
214
197
  };
215
198
 
216
199
  export const ProtectedRolesEditPage = () => (
217
- <CheckPagePermissions permissions={PERMISSIONS.updateRole}>
200
+ <Page.Protect permissions={PERMISSIONS.updateRole}>
218
201
  <EditPage />
219
- </CheckPagePermissions>
202
+ </Page.Protect>
220
203
  );
@@ -1,24 +1,24 @@
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';
4
+ import { onRowClick, stopPropagation } from '@strapi/helper-plugin';
5
5
  import { Pencil, Trash } from '@strapi/icons';
6
6
  import PropTypes from 'prop-types';
7
7
  import { useIntl } from 'react-intl';
8
- import { useHistory } from 'react-router-dom';
8
+ import { useNavigate } from 'react-router-dom';
9
9
  import styled from 'styled-components';
10
10
 
11
11
  const EditLink = styled(Link)`
12
12
  align-items: center;
13
- height: ${pxToRem(32)};
13
+ height: ${32 / 16}rem;
14
14
  display: flex;
15
15
  justify-content: center;
16
16
  padding: ${({ theme }) => `${theme.spaces[2]}}`};
17
- width: ${pxToRem(32)};
17
+ width: ${32 / 16}rem;
18
18
 
19
19
  svg {
20
- height: ${pxToRem(12)};
21
- width: ${pxToRem(12)};
20
+ height: ${12 / 16}rem;
21
+ width: ${12 / 16}rem;
22
22
 
23
23
  path {
24
24
  fill: ${({ theme }) => theme.colors.neutral500};
@@ -35,9 +35,9 @@ const EditLink = styled(Link)`
35
35
  }
36
36
  `;
37
37
 
38
- const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDelete }) => {
38
+ const TableBody = ({ sortedRoles, canDelete, canUpdate, setRoleToDelete, onDelete }) => {
39
39
  const { formatMessage } = useIntl();
40
- const { push } = useHistory();
40
+ const navigate = useNavigate();
41
41
  const [showConfirmDelete, setShowConfirmDelete] = onDelete;
42
42
 
43
43
  const checkCanDeleteRole = (role) =>
@@ -48,14 +48,10 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
48
48
  setShowConfirmDelete(!showConfirmDelete);
49
49
  };
50
50
 
51
- const handleClickEdit = (id) => {
52
- push(`/settings/users-permissions/roles/${id}`);
53
- };
54
-
55
51
  return (
56
52
  <Tbody>
57
53
  {sortedRoles?.map((role) => (
58
- <Tr key={role.name} {...onRowClick({ fn: () => handleClickEdit(role.id) })}>
54
+ <Tr key={role.name} {...onRowClick({ fn: () => navigate(role.id.toString()) })}>
59
55
  <Td width="20%">
60
56
  <Typography>{role.name}</Typography>
61
57
  </Td>
@@ -75,9 +71,9 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
75
71
  </Td>
76
72
  <Td>
77
73
  <Flex justifyContent="end" {...stopPropagation}>
78
- <CheckPermissions permissions={permissions.updateRole}>
74
+ {canUpdate ? (
79
75
  <EditLink
80
- to={`/settings/users-permissions/roles/${role.id}`}
76
+ to={role.id.toString()}
81
77
  aria-label={formatMessage(
82
78
  { id: 'app.component.table.edit', defaultMessage: 'Edit {target}' },
83
79
  { target: `${role.name}` }
@@ -85,20 +81,18 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
85
81
  >
86
82
  <Pencil />
87
83
  </EditLink>
88
- </CheckPermissions>
84
+ ) : null}
89
85
 
90
86
  {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>
87
+ <IconButton
88
+ onClick={() => handleClickDelete(role.id.toString())}
89
+ noBorder
90
+ icon={<Trash />}
91
+ label={formatMessage(
92
+ { id: 'global.delete-target', defaultMessage: 'Delete {target}' },
93
+ { target: `${role.name}` }
94
+ )}
95
+ />
102
96
  )}
103
97
  </Flex>
104
98
  </Td>
@@ -112,6 +106,7 @@ export default TableBody;
112
106
 
113
107
  TableBody.defaultProps = {
114
108
  canDelete: false,
109
+ canUpdate: false,
115
110
  };
116
111
 
117
112
  TableBody.propTypes = {
@@ -120,4 +115,5 @@ TableBody.propTypes = {
120
115
  setRoleToDelete: PropTypes.func.isRequired,
121
116
  sortedRoles: PropTypes.array.isRequired,
122
117
  canDelete: PropTypes.bool,
118
+ canUpdate: PropTypes.bool,
123
119
  };
@@ -13,28 +13,25 @@ import {
13
13
  Typography,
14
14
  useNotifyAT,
15
15
  VisuallyHidden,
16
- } from '@strapi/design-system';
17
- import {
18
- CheckPagePermissions,
19
- CheckPermissions,
20
- ConfirmDialog,
21
16
  EmptyStateLayout,
22
- LinkButton,
23
- LoadingIndicatorPage,
24
- NoPermissions,
25
- SearchURLQuery,
26
- SettingsPageTitle,
27
17
  useCollator,
28
18
  useFilter,
29
- useFocusWhenNavigate,
30
- useNotification,
31
- useQueryParams,
32
- useRBAC,
33
- useTracking,
34
- } from '@strapi/helper-plugin';
19
+ } from '@strapi/design-system';
20
+ import { LinkButton } from '@strapi/design-system/v2';
21
+ import { useFocusWhenNavigate, useQueryParams, useRBAC } from '@strapi/helper-plugin';
35
22
  import { Plus } from '@strapi/icons';
23
+ import {
24
+ ConfirmDialog,
25
+ useTracking,
26
+ Page,
27
+ SearchInput,
28
+ BackButton,
29
+ useNotification,
30
+ } from '@strapi/strapi/admin';
31
+ import { Helmet } from 'react-helmet';
36
32
  import { useIntl } from 'react-intl';
37
33
  import { useMutation, useQuery } from 'react-query';
34
+ import { NavLink } from 'react-router-dom';
38
35
 
39
36
  import { PERMISSIONS } from '../../../../constants';
40
37
  import { getTrad } from '../../../../utils';
@@ -45,18 +42,17 @@ import { deleteData, fetchData } from './utils/api';
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
51
  useFocusWhenNavigate();
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,7 +81,7 @@ 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);
@@ -107,21 +103,19 @@ export const RolesListPage = () => {
107
103
  defaultMessage: 'Roles',
108
104
  });
109
105
 
110
- const deleteMutation = useMutation((id) => deleteData(id, toggleNotification), {
106
+ const deleteMutation = useMutation((id) => deleteData(id, formatMessage, toggleNotification), {
111
107
  async onSuccess() {
112
108
  await refetch();
113
109
  },
114
110
  });
115
111
 
116
112
  const handleConfirmDelete = async () => {
117
- setIsConfirmButtonLoading(true);
118
113
  await deleteMutation.mutateAsync(roleToDelete);
119
114
  setShowConfirmDelete(!showConfirmDelete);
120
- setIsConfirmButtonLoading(false);
121
115
  };
122
116
 
123
117
  const sortedRoles = (roles || [])
124
- .filter((role) => includes(role.name, _q) || includes(role.description, _q))
118
+ .filter((role) => contains(role.name, _q) || contains(role.description, _q))
125
119
  .sort(
126
120
  (a, b) => formatter.compare(a.name, b.name) || formatter.compare(a.description, b.description)
127
121
  );
@@ -131,9 +125,18 @@ export const RolesListPage = () => {
131
125
  const colCount = 4;
132
126
  const rowCount = (roles?.length || 0) + 1;
133
127
 
128
+ if (isLoading) {
129
+ return <Page.Loading />;
130
+ }
131
+
134
132
  return (
135
133
  <Layout>
136
- <SettingsPageTitle name={pageTitle} />
134
+ <Helmet
135
+ title={formatMessage(
136
+ { id: 'Settings.PageTitle', defaultMessage: 'Settings - {name}' },
137
+ { name: pageTitle }
138
+ )}
139
+ />
137
140
  <Main aria-busy={isLoading}>
138
141
  <HeaderLayout
139
142
  title={formatMessage({
@@ -145,9 +148,10 @@ export const RolesListPage = () => {
145
148
  defaultMessage: 'List of roles',
146
149
  })}
147
150
  primaryAction={
148
- <CheckPermissions permissions={PERMISSIONS.createRole}>
151
+ canCreate ? (
149
152
  <LinkButton
150
- to="/settings/users-permissions/roles/new"
153
+ to="new"
154
+ as={NavLink}
151
155
  onClick={() => trackUsage('willCreateRole')}
152
156
  startIcon={<Plus />}
153
157
  size="S"
@@ -157,13 +161,14 @@ export const RolesListPage = () => {
157
161
  defaultMessage: 'Add new role',
158
162
  })}
159
163
  </LinkButton>
160
- </CheckPermissions>
164
+ ) : null
161
165
  }
166
+ navigationAction={<BackButton />}
162
167
  />
163
168
 
164
169
  <ActionLayout
165
170
  startActions={
166
- <SearchURLQuery
171
+ <SearchInput
167
172
  label={formatMessage({
168
173
  id: 'app.component.search.label',
169
174
  defaultMessage: 'Search',
@@ -173,8 +178,7 @@ export const RolesListPage = () => {
173
178
  />
174
179
 
175
180
  <ContentLayout>
176
- {!canRead && <NoPermissions />}
177
- {(isLoading || isLoadingForPermissions) && <LoadingIndicatorPage />}
181
+ {!canRead && <Page.NoPermissions />}
178
182
  {canRead && sortedRoles && sortedRoles?.length ? (
179
183
  <Table colCount={colCount} rowCount={rowCount}>
180
184
  <Thead>
@@ -213,19 +217,19 @@ export const RolesListPage = () => {
213
217
  <TableBody
214
218
  sortedRoles={sortedRoles}
215
219
  canDelete={canDelete}
220
+ canUpdate={canUpdate}
216
221
  permissions={PERMISSIONS}
217
222
  setRoleToDelete={setRoleToDelete}
218
223
  onDelete={[showConfirmDelete, setShowConfirmDelete]}
219
224
  />
220
225
  </Table>
221
226
  ) : (
222
- <EmptyStateLayout content={emptyLayout[emptyContent]} />
227
+ <EmptyStateLayout content={formatMessage(emptyLayout[emptyContent])} />
223
228
  )}
224
229
  </ContentLayout>
225
230
  <ConfirmDialog
226
- isConfirmButtonLoading={isConfirmButtonLoading}
227
231
  onConfirm={handleConfirmDelete}
228
- onToggleDialog={handleShowConfirmDelete}
232
+ onClose={handleShowConfirmDelete}
229
233
  isOpen={showConfirmDelete}
230
234
  />
231
235
  </Main>
@@ -235,8 +239,8 @@ export const RolesListPage = () => {
235
239
 
236
240
  export const ProtectedRolesListPage = () => {
237
241
  return (
238
- <CheckPagePermissions permissions={PERMISSIONS.accessRoles}>
242
+ <Page.Protect permissions={PERMISSIONS.accessRoles}>
239
243
  <RolesListPage />
240
- </CheckPagePermissions>
244
+ </Page.Protect>
241
245
  );
242
246
  };
@@ -1,6 +1,6 @@
1
1
  import { getFetchClient } from '@strapi/helper-plugin';
2
2
 
3
- export const fetchData = async (toggleNotification, notifyStatus) => {
3
+ export const fetchData = async (toggleNotification, formatMessage, notifyStatus) => {
4
4
  try {
5
5
  const { get } = getFetchClient();
6
6
  const { data } = await get('/users-permissions/roles');
@@ -9,22 +9,22 @@ export const fetchData = async (toggleNotification, notifyStatus) => {
9
9
  return data;
10
10
  } catch (err) {
11
11
  toggleNotification({
12
- type: 'warning',
13
- message: { id: 'notification.error' },
12
+ type: 'danger',
13
+ message: formatMessage({ id: 'notification.error' }),
14
14
  });
15
15
 
16
16
  throw new Error(err);
17
17
  }
18
18
  };
19
19
 
20
- export const deleteData = async (id, toggleNotification) => {
20
+ export const deleteData = async (id, formatMessage, toggleNotification) => {
21
21
  try {
22
22
  const { del } = getFetchClient();
23
23
  await del(`/users-permissions/roles/${id}`);
24
24
  } catch (error) {
25
25
  toggleNotification({
26
- type: 'warning',
27
- message: { id: 'notification.error', defaultMessage: 'An error occured' },
26
+ type: 'danger',
27
+ message: formatMessage({ id: 'notification.error', defaultMessage: 'An error occured' }),
28
28
  });
29
29
  }
30
30
  };
@@ -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.",