@strapi/plugin-users-permissions 0.0.0-next.ddc227cf71596473e8e744b0f90388b054d07e45 → 0.0.0-next.de77e236e318525454da54a1a2617d86742e6e6c

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 (182) hide show
  1. package/.eslintignore +1 -2
  2. package/.eslintrc +17 -0
  3. package/admin/src/components/Permissions/{index.js → index.jsx} +2 -4
  4. package/admin/src/index.js +12 -23
  5. package/admin/src/pages/AdvancedSettings/{index.js → index.jsx} +44 -28
  6. package/admin/src/pages/EmailTemplates/{index.js → index.jsx} +64 -53
  7. package/admin/src/pages/Providers/{index.js → index.jsx} +64 -62
  8. package/admin/src/{hooks → pages/Roles/hooks}/usePlugins.js +15 -8
  9. package/admin/src/pages/Roles/{index.js → index.jsx} +10 -7
  10. package/admin/src/pages/Roles/pages/CreatePage.jsx +199 -0
  11. package/admin/src/pages/Roles/pages/EditPage.jsx +220 -0
  12. package/admin/src/pages/Roles/{ListPage/components/TableBody.js → pages/ListPage/components/TableBody.jsx} +44 -14
  13. package/admin/src/pages/Roles/{ListPage/index.js → pages/ListPage/index.jsx} +29 -30
  14. package/admin/src/pages/Roles/{ListPage → pages/ListPage}/utils/api.js +2 -4
  15. package/admin/src/translations/zh-Hans.json +80 -80
  16. package/admin/src/utils/index.js +0 -1
  17. package/dist/_chunks/ar-MvD8Ghac.mjs +44 -0
  18. package/dist/_chunks/ar-MvD8Ghac.mjs.map +1 -0
  19. package/dist/_chunks/ar-t5qTFaAD.js +44 -0
  20. package/dist/_chunks/ar-t5qTFaAD.js.map +1 -0
  21. package/dist/_chunks/cs-BMuXwxA1.mjs +50 -0
  22. package/dist/_chunks/cs-BMuXwxA1.mjs.map +1 -0
  23. package/dist/_chunks/cs-I8N4u-Sd.js +50 -0
  24. package/dist/_chunks/cs-I8N4u-Sd.js.map +1 -0
  25. package/dist/_chunks/de-YTjtq89K.js +62 -0
  26. package/dist/_chunks/de-YTjtq89K.js.map +1 -0
  27. package/dist/_chunks/de-zs2qqc0W.mjs +62 -0
  28. package/dist/_chunks/de-zs2qqc0W.mjs.map +1 -0
  29. package/dist/_chunks/dk-HctVBMsG.mjs +86 -0
  30. package/dist/_chunks/dk-HctVBMsG.mjs.map +1 -0
  31. package/dist/_chunks/dk-TF-dWjzl.js +86 -0
  32. package/dist/_chunks/dk-TF-dWjzl.js.map +1 -0
  33. package/dist/_chunks/en-CE3wEy_c.mjs +86 -0
  34. package/dist/_chunks/en-CE3wEy_c.mjs.map +1 -0
  35. package/dist/_chunks/en-m608rMZx.js +86 -0
  36. package/dist/_chunks/en-m608rMZx.js.map +1 -0
  37. package/dist/_chunks/es-9381tih_.mjs +86 -0
  38. package/dist/_chunks/es-9381tih_.mjs.map +1 -0
  39. package/dist/_chunks/es-XBQsB8_9.js +86 -0
  40. package/dist/_chunks/es-XBQsB8_9.js.map +1 -0
  41. package/dist/_chunks/fr-6cz3U-IF.js +50 -0
  42. package/dist/_chunks/fr-6cz3U-IF.js.map +1 -0
  43. package/dist/_chunks/fr-CMSc77If.mjs +50 -0
  44. package/dist/_chunks/fr-CMSc77If.mjs.map +1 -0
  45. package/dist/_chunks/id-RJ934rq-.js +62 -0
  46. package/dist/_chunks/id-RJ934rq-.js.map +1 -0
  47. package/dist/_chunks/id-SDuyIkZa.mjs +62 -0
  48. package/dist/_chunks/id-SDuyIkZa.mjs.map +1 -0
  49. package/dist/_chunks/index-6EnldfNI.mjs +615 -0
  50. package/dist/_chunks/index-6EnldfNI.mjs.map +1 -0
  51. package/dist/_chunks/index-6GwHcGdL.js +249 -0
  52. package/dist/_chunks/index-6GwHcGdL.js.map +1 -0
  53. package/dist/_chunks/index-BJ2dBu2N.mjs +250 -0
  54. package/dist/_chunks/index-BJ2dBu2N.mjs.map +1 -0
  55. package/dist/_chunks/index-KNxvhY9E.js +1191 -0
  56. package/dist/_chunks/index-KNxvhY9E.js.map +1 -0
  57. package/dist/_chunks/index-U1clXdiG.mjs +1159 -0
  58. package/dist/_chunks/index-U1clXdiG.mjs.map +1 -0
  59. package/dist/_chunks/index-V7crIIIK.js +320 -0
  60. package/dist/_chunks/index-V7crIIIK.js.map +1 -0
  61. package/dist/_chunks/index-jpgayepG.js +407 -0
  62. package/dist/_chunks/index-jpgayepG.js.map +1 -0
  63. package/dist/_chunks/index-ko6yRyEd.js +638 -0
  64. package/dist/_chunks/index-ko6yRyEd.js.map +1 -0
  65. package/dist/_chunks/index-wDWfjd3v.mjs +385 -0
  66. package/dist/_chunks/index-wDWfjd3v.mjs.map +1 -0
  67. package/dist/_chunks/index-xcuiqIT8.mjs +301 -0
  68. package/dist/_chunks/index-xcuiqIT8.mjs.map +1 -0
  69. package/dist/_chunks/it-YhZOlM2X.js +62 -0
  70. package/dist/_chunks/it-YhZOlM2X.js.map +1 -0
  71. package/dist/_chunks/it-bvH7DgQo.mjs +62 -0
  72. package/dist/_chunks/it-bvH7DgQo.mjs.map +1 -0
  73. package/dist/_chunks/ja-o_-JPvQv.mjs +48 -0
  74. package/dist/_chunks/ja-o_-JPvQv.mjs.map +1 -0
  75. package/dist/_chunks/ja-xssHUXFv.js +48 -0
  76. package/dist/_chunks/ja-xssHUXFv.js.map +1 -0
  77. package/dist/_chunks/ko-C3mHUSJa.js +86 -0
  78. package/dist/_chunks/ko-C3mHUSJa.js.map +1 -0
  79. package/dist/_chunks/ko-XJbPSez_.mjs +86 -0
  80. package/dist/_chunks/ko-XJbPSez_.mjs.map +1 -0
  81. package/dist/_chunks/ms-II5Ea73J.mjs +49 -0
  82. package/dist/_chunks/ms-II5Ea73J.mjs.map +1 -0
  83. package/dist/_chunks/ms-d0hfg65Z.js +49 -0
  84. package/dist/_chunks/ms-d0hfg65Z.js.map +1 -0
  85. package/dist/_chunks/nl-TA7TfK_5.js +48 -0
  86. package/dist/_chunks/nl-TA7TfK_5.js.map +1 -0
  87. package/dist/_chunks/nl-vEy6TN0K.mjs +48 -0
  88. package/dist/_chunks/nl-vEy6TN0K.mjs.map +1 -0
  89. package/dist/_chunks/pl-0pUL9hdA.js +86 -0
  90. package/dist/_chunks/pl-0pUL9hdA.js.map +1 -0
  91. package/dist/_chunks/pl-2VowaFGt.mjs +86 -0
  92. package/dist/_chunks/pl-2VowaFGt.mjs.map +1 -0
  93. package/dist/_chunks/pt-BR-WNOhafR4.js +44 -0
  94. package/dist/_chunks/pt-BR-WNOhafR4.js.map +1 -0
  95. package/dist/_chunks/pt-BR-sS1Xp3Jt.mjs +44 -0
  96. package/dist/_chunks/pt-BR-sS1Xp3Jt.mjs.map +1 -0
  97. package/dist/_chunks/pt-Rf9W51IO.mjs +48 -0
  98. package/dist/_chunks/pt-Rf9W51IO.mjs.map +1 -0
  99. package/dist/_chunks/pt-guNR9Gax.js +48 -0
  100. package/dist/_chunks/pt-guNR9Gax.js.map +1 -0
  101. package/dist/_chunks/ru-X3BMXDds.js +86 -0
  102. package/dist/_chunks/ru-X3BMXDds.js.map +1 -0
  103. package/dist/_chunks/ru-qKHnd5or.mjs +86 -0
  104. package/dist/_chunks/ru-qKHnd5or.mjs.map +1 -0
  105. package/dist/_chunks/sk-NWPw1oTN.js +50 -0
  106. package/dist/_chunks/sk-NWPw1oTN.js.map +1 -0
  107. package/dist/_chunks/sk-_Ryr-eTT.mjs +50 -0
  108. package/dist/_chunks/sk-_Ryr-eTT.mjs.map +1 -0
  109. package/dist/_chunks/sv-76NnbB__.js +86 -0
  110. package/dist/_chunks/sv-76NnbB__.js.map +1 -0
  111. package/dist/_chunks/sv-BqzScFXS.mjs +86 -0
  112. package/dist/_chunks/sv-BqzScFXS.mjs.map +1 -0
  113. package/dist/_chunks/th-WsknMEpq.mjs +60 -0
  114. package/dist/_chunks/th-WsknMEpq.mjs.map +1 -0
  115. package/dist/_chunks/th-cbppX21D.js +60 -0
  116. package/dist/_chunks/th-cbppX21D.js.map +1 -0
  117. package/dist/_chunks/tr-6mm_Fmz7.js +85 -0
  118. package/dist/_chunks/tr-6mm_Fmz7.js.map +1 -0
  119. package/dist/_chunks/tr-_DB1F1GW.mjs +85 -0
  120. package/dist/_chunks/tr-_DB1F1GW.mjs.map +1 -0
  121. package/dist/_chunks/uk-sI2I1ogF.js +49 -0
  122. package/dist/_chunks/uk-sI2I1ogF.js.map +1 -0
  123. package/dist/_chunks/uk-yxMSQAwI.mjs +49 -0
  124. package/dist/_chunks/uk-yxMSQAwI.mjs.map +1 -0
  125. package/dist/_chunks/vi-A3zJxaiI.js +50 -0
  126. package/dist/_chunks/vi-A3zJxaiI.js.map +1 -0
  127. package/dist/_chunks/vi-xY0zCW3d.mjs +50 -0
  128. package/dist/_chunks/vi-xY0zCW3d.mjs.map +1 -0
  129. package/dist/_chunks/zh-72SpmFXa.js +86 -0
  130. package/dist/_chunks/zh-72SpmFXa.js.map +1 -0
  131. package/dist/_chunks/zh-Hans-ArWWtyP4.js +86 -0
  132. package/dist/_chunks/zh-Hans-ArWWtyP4.js.map +1 -0
  133. package/dist/_chunks/zh-Hans-E84cu4kP.mjs +86 -0
  134. package/dist/_chunks/zh-Hans-E84cu4kP.mjs.map +1 -0
  135. package/dist/_chunks/zh-OFeldzbX.mjs +86 -0
  136. package/dist/_chunks/zh-OFeldzbX.mjs.map +1 -0
  137. package/dist/admin/index.js +5 -0
  138. package/dist/admin/index.js.map +1 -0
  139. package/dist/admin/index.mjs +6 -0
  140. package/dist/admin/index.mjs.map +1 -0
  141. package/documentation/content-api.yaml +1 -1
  142. package/jest.config.front.js +1 -1
  143. package/package.json +37 -17
  144. package/packup.config.ts +22 -0
  145. package/server/bootstrap/grant-config.js +9 -0
  146. package/server/bootstrap/index.js +36 -0
  147. package/server/controllers/auth.js +51 -14
  148. package/server/controllers/user.js +12 -1
  149. package/server/middlewares/rateLimit.js +41 -21
  150. package/server/services/providers-registry.js +15 -0
  151. package/.eslintrc.js +0 -14
  152. package/admin/src/hooks/index.js +0 -5
  153. package/admin/src/hooks/useFetchRole/index.js +0 -67
  154. package/admin/src/hooks/useFetchRole/reducer.js +0 -31
  155. package/admin/src/hooks/useForm/index.js +0 -70
  156. package/admin/src/hooks/useForm/reducer.js +0 -40
  157. package/admin/src/hooks/useRolesList/index.js +0 -65
  158. package/admin/src/hooks/useRolesList/init.js +0 -5
  159. package/admin/src/hooks/useRolesList/reducer.js +0 -31
  160. package/admin/src/pages/AdvancedSettings/utils/api.js +0 -18
  161. package/admin/src/pages/EmailTemplates/utils/api.js +0 -18
  162. package/admin/src/pages/Providers/reducer.js +0 -54
  163. package/admin/src/pages/Providers/utils/api.js +0 -26
  164. package/admin/src/pages/Providers/utils/createProvidersArray.js +0 -21
  165. package/admin/src/pages/Roles/CreatePage.js +0 -185
  166. package/admin/src/pages/Roles/EditPage.js +0 -197
  167. package/admin/src/pages/Roles/ProtectedCreatePage.js +0 -15
  168. package/admin/src/pages/Roles/ProtectedEditPage.js +0 -15
  169. package/admin/src/pages/Roles/ProtectedListPage.js +0 -17
  170. package/admin/src/utils/getRequestURL.js +0 -5
  171. package/strapi-admin.js +0 -3
  172. /package/admin/src/components/BoundRoute/{index.js → index.jsx} +0 -0
  173. /package/admin/src/components/FormModal/Input/{index.js → index.jsx} +0 -0
  174. /package/admin/src/components/FormModal/{index.js → index.jsx} +0 -0
  175. /package/admin/src/components/Permissions/PermissionRow/{CheckboxWrapper.js → CheckboxWrapper.jsx} +0 -0
  176. /package/admin/src/components/Permissions/PermissionRow/{SubCategory.js → SubCategory.jsx} +0 -0
  177. /package/admin/src/components/Permissions/PermissionRow/{index.js → index.jsx} +0 -0
  178. /package/admin/src/components/Policies/{index.js → index.jsx} +0 -0
  179. /package/admin/src/components/UsersPermissions/{index.js → index.jsx} +0 -0
  180. /package/admin/src/contexts/UsersPermissionsContext/{index.js → index.jsx} +0 -0
  181. /package/admin/src/pages/EmailTemplates/components/{EmailForm.js → EmailForm.jsx} +0 -0
  182. /package/admin/src/pages/EmailTemplates/components/{EmailTable.js → EmailTable.jsx} +0 -0
@@ -3,8 +3,7 @@ import { useEffect } from 'react';
3
3
  import { useNotification, useFetchClient, useAPIErrorHandler } from '@strapi/helper-plugin';
4
4
  import { useQueries } from 'react-query';
5
5
 
6
- import pluginId from '../pluginId';
7
- import { cleanPermissions, getTrad } from '../utils';
6
+ import { cleanPermissions, getTrad } from '../../../utils';
8
7
 
9
8
  export const usePlugins = () => {
10
9
  const toggleNotification = useNotification();
@@ -21,19 +20,23 @@ export const usePlugins = () => {
21
20
  { data: routes, isLoading: isLoadingRoutes, error: routesError, refetch: refetchRoutes },
22
21
  ] = useQueries([
23
22
  {
24
- queryKey: [pluginId, 'permissions'],
23
+ queryKey: ['users-permissions', 'permissions'],
25
24
  async queryFn() {
26
- const res = await get(`/${pluginId}/permissions`);
25
+ const {
26
+ data: { permissions },
27
+ } = await get(`/users-permissions/permissions`);
27
28
 
28
- return res.data.permissions;
29
+ return permissions;
29
30
  },
30
31
  },
31
32
  {
32
- queryKey: [pluginId, 'routes'],
33
+ queryKey: ['users-permissions', 'routes'],
33
34
  async queryFn() {
34
- const res = await get(`/${pluginId}/routes`);
35
+ const {
36
+ data: { routes },
37
+ } = await get(`/users-permissions/routes`);
35
38
 
36
- return res.data.routes;
39
+ return routes;
37
40
  },
38
41
  },
39
42
  ]);
@@ -63,8 +66,12 @@ export const usePlugins = () => {
63
66
  const isLoading = isLoadingPermissions || isLoadingRoutes;
64
67
 
65
68
  return {
69
+ // TODO: these return values need to be memoized, otherwise
70
+ // they will create infinite rendering loops when used as
71
+ // effect dependencies
66
72
  permissions: permissions ? cleanPermissions(permissions) : {},
67
73
  routes: routes ?? {},
74
+
68
75
  getData: refetchQueries,
69
76
  isLoading,
70
77
  };
@@ -4,23 +4,26 @@ import { AnErrorOccurred, CheckPagePermissions } from '@strapi/helper-plugin';
4
4
  import { Route, Switch } from 'react-router-dom';
5
5
 
6
6
  import { PERMISSIONS } from '../../constants';
7
- import pluginId from '../../pluginId';
8
7
 
9
- import ProtectedRolesCreatePage from './ProtectedCreatePage';
10
- import ProtectedRolesEditPage from './ProtectedEditPage';
11
- import ProtectedRolesListPage from './ProtectedListPage';
8
+ import { ProtectedRolesCreatePage } from './pages/CreatePage';
9
+ import { ProtectedRolesEditPage } from './pages/EditPage';
10
+ import { ProtectedRolesListPage } from './pages/ListPage';
12
11
 
13
12
  const Roles = () => {
14
13
  return (
15
14
  <CheckPagePermissions permissions={PERMISSIONS.accessRoles}>
16
15
  <Switch>
17
16
  <Route
18
- path={`/settings/${pluginId}/roles/new`}
17
+ path="/settings/users-permissions/roles/new"
19
18
  component={ProtectedRolesCreatePage}
20
19
  exact
21
20
  />
22
- <Route path={`/settings/${pluginId}/roles/:id`} component={ProtectedRolesEditPage} exact />
23
- <Route path={`/settings/${pluginId}/roles`} component={ProtectedRolesListPage} exact />
21
+ <Route
22
+ path="/settings/users-permissions/roles/:id"
23
+ component={ProtectedRolesEditPage}
24
+ exact
25
+ />
26
+ <Route path="/settings/users-permissions/roles" component={ProtectedRolesListPage} exact />
24
27
  <Route path="" component={AnErrorOccurred} />
25
28
  </Switch>
26
29
  </CheckPagePermissions>
@@ -0,0 +1,199 @@
1
+ import * as React from 'react';
2
+
3
+ import {
4
+ Button,
5
+ ContentLayout,
6
+ Flex,
7
+ Grid,
8
+ GridItem,
9
+ HeaderLayout,
10
+ Main,
11
+ Textarea,
12
+ TextInput,
13
+ Typography,
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
+ import { Check } from '@strapi/icons';
25
+ import { Formik } from 'formik';
26
+ import { useIntl } from 'react-intl';
27
+ import { useMutation } from 'react-query';
28
+ import { useHistory } from 'react-router-dom';
29
+
30
+ import UsersPermissions from '../../../components/UsersPermissions';
31
+ import { PERMISSIONS } from '../../../constants';
32
+ import getTrad from '../../../utils/getTrad';
33
+ import { createRoleSchema } from '../constants';
34
+ import { usePlugins } from '../hooks/usePlugins';
35
+
36
+ export const CreatePage = () => {
37
+ const { formatMessage } = useIntl();
38
+ const toggleNotification = useNotification();
39
+ const { goBack } = useHistory();
40
+ const { lockApp, unlockApp } = useOverlayBlocker();
41
+ const { isLoading: isLoadingPlugins, permissions, routes } = usePlugins();
42
+ const { trackUsage } = useTracking();
43
+ const permissionsRef = React.useRef();
44
+ const { post } = useFetchClient();
45
+ const mutation = useMutation((body) => post(`/users-permissions/roles`, body), {
46
+ onError() {
47
+ toggleNotification({
48
+ type: 'warning',
49
+ message: {
50
+ id: 'notification.error',
51
+ defaultMessage: 'An error occurred',
52
+ },
53
+ });
54
+ },
55
+
56
+ onSuccess() {
57
+ trackUsage('didCreateRole');
58
+
59
+ toggleNotification({
60
+ type: 'success',
61
+ message: {
62
+ id: getTrad('Settings.roles.created'),
63
+ defaultMessage: 'Role created',
64
+ },
65
+ });
66
+
67
+ // Forcing redirecting since we don't have the id in the response
68
+ goBack();
69
+ },
70
+ });
71
+
72
+ const handleCreateRoleSubmit = async (data) => {
73
+ lockApp();
74
+
75
+ // TODO: refactor. Child -> parent component communication is evil;
76
+ // We should either move the provider one level up or move the state
77
+ // straight into redux.
78
+ const permissions = permissionsRef.current.getPermissions();
79
+
80
+ await mutation.mutate({ ...data, ...permissions, users: [] });
81
+
82
+ unlockApp();
83
+ };
84
+
85
+ return (
86
+ <Main>
87
+ {/* TODO: This needs to be translated */}
88
+ <SettingsPageTitle name="Roles" />
89
+ <Formik
90
+ enableReinitialize
91
+ initialValues={{ name: '', description: '' }}
92
+ onSubmit={handleCreateRoleSubmit}
93
+ validationSchema={createRoleSchema}
94
+ >
95
+ {({ handleSubmit, values, handleChange, errors }) => (
96
+ <Form noValidate onSubmit={handleSubmit}>
97
+ <HeaderLayout
98
+ primaryAction={
99
+ !isLoadingPlugins && (
100
+ <Button type="submit" loading={mutation.isLoading} startIcon={<Check />}>
101
+ {formatMessage({
102
+ id: 'global.save',
103
+ defaultMessage: 'Save',
104
+ })}
105
+ </Button>
106
+ )
107
+ }
108
+ title={formatMessage({
109
+ id: 'Settings.roles.create.title',
110
+ defaultMessage: 'Create a role',
111
+ })}
112
+ subtitle={formatMessage({
113
+ id: 'Settings.roles.create.description',
114
+ defaultMessage: 'Define the rights given to the role',
115
+ })}
116
+ />
117
+ <ContentLayout>
118
+ <Flex
119
+ background="neutral0"
120
+ direction="column"
121
+ alignItems="stretch"
122
+ gap={7}
123
+ hasRadius
124
+ paddingTop={6}
125
+ paddingBottom={6}
126
+ paddingLeft={7}
127
+ paddingRight={7}
128
+ shadow="filterShadow"
129
+ >
130
+ <Flex direction="column" alignItems="stretch">
131
+ <Typography variant="delta" as="h2">
132
+ {formatMessage({
133
+ id: getTrad('EditPage.form.roles'),
134
+ defaultMessage: 'Role details',
135
+ })}
136
+ </Typography>
137
+
138
+ <Grid gap={4}>
139
+ <GridItem col={6}>
140
+ <TextInput
141
+ name="name"
142
+ value={values.name || ''}
143
+ onChange={handleChange}
144
+ label={formatMessage({
145
+ id: 'global.name',
146
+ defaultMessage: 'Name',
147
+ })}
148
+ error={
149
+ errors?.name
150
+ ? formatMessage({ id: errors.name, defaultMessage: 'Name is required' })
151
+ : false
152
+ }
153
+ required
154
+ />
155
+ </GridItem>
156
+ <GridItem col={6}>
157
+ <Textarea
158
+ id="description"
159
+ value={values.description || ''}
160
+ onChange={handleChange}
161
+ label={formatMessage({
162
+ id: 'global.description',
163
+ defaultMessage: 'Description',
164
+ })}
165
+ error={
166
+ errors?.description
167
+ ? formatMessage({
168
+ id: errors.description,
169
+ defaultMessage: 'Description is required',
170
+ })
171
+ : false
172
+ }
173
+ required
174
+ />
175
+ </GridItem>
176
+ </Grid>
177
+ </Flex>
178
+
179
+ {!isLoadingPlugins && (
180
+ <UsersPermissions
181
+ ref={permissionsRef}
182
+ permissions={permissions}
183
+ routes={routes}
184
+ />
185
+ )}
186
+ </Flex>
187
+ </ContentLayout>
188
+ </Form>
189
+ )}
190
+ </Formik>
191
+ </Main>
192
+ );
193
+ };
194
+
195
+ export const ProtectedRolesCreatePage = () => (
196
+ <CheckPagePermissions permissions={PERMISSIONS.createRole}>
197
+ <CreatePage />
198
+ </CheckPagePermissions>
199
+ );
@@ -0,0 +1,220 @@
1
+ import * as React from 'react';
2
+
3
+ import {
4
+ ContentLayout,
5
+ HeaderLayout,
6
+ Main,
7
+ Button,
8
+ Flex,
9
+ TextInput,
10
+ Textarea,
11
+ Typography,
12
+ GridItem,
13
+ Grid,
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';
28
+ import { useIntl } from 'react-intl';
29
+ import { useQuery, useMutation } from 'react-query';
30
+ import { useRouteMatch } from 'react-router-dom';
31
+
32
+ import UsersPermissions from '../../../components/UsersPermissions';
33
+ import { PERMISSIONS } from '../../../constants';
34
+ import getTrad from '../../../utils/getTrad';
35
+ import { createRoleSchema } from '../constants';
36
+ import { usePlugins } from '../hooks/usePlugins';
37
+
38
+ export const EditPage = () => {
39
+ const { formatMessage } = useIntl();
40
+ const toggleNotification = useNotification();
41
+ const { lockApp, unlockApp } = useOverlayBlocker();
42
+ const {
43
+ params: { id },
44
+ } = useRouteMatch(`/settings/users-permissions/roles/:id`);
45
+ const { get } = useFetchClient();
46
+ const { isLoading: isLoadingPlugins, routes } = usePlugins();
47
+ const {
48
+ data: role,
49
+ isLoading: isLoadingRole,
50
+ refetch: refetchRole,
51
+ } = useQuery(['users-permissions', 'role', id], async () => {
52
+ // TODO: why doesn't this endpoint follow the admin API conventions?
53
+ const {
54
+ data: { role },
55
+ } = await get(`/users-permissions/roles/${id}`);
56
+
57
+ return role;
58
+ });
59
+
60
+ const permissionsRef = React.useRef();
61
+ const { put } = useFetchClient();
62
+ const { formatAPIError } = useAPIErrorHandler();
63
+ const mutation = useMutation((body) => put(`/users-permissions/roles/${id}`, body), {
64
+ onError(error) {
65
+ toggleNotification({
66
+ type: 'warning',
67
+ message: formatAPIError(error),
68
+ });
69
+ },
70
+
71
+ async onSuccess() {
72
+ toggleNotification({
73
+ type: 'success',
74
+ message: {
75
+ id: getTrad('Settings.roles.created'),
76
+ defaultMessage: 'Role edited',
77
+ },
78
+ });
79
+
80
+ await refetchRole();
81
+ },
82
+ });
83
+
84
+ const handleEditRoleSubmit = async (data) => {
85
+ // Set loading state
86
+ lockApp();
87
+
88
+ const permissions = permissionsRef.current.getPermissions();
89
+
90
+ await mutation.mutate({ ...data, ...permissions, users: [] });
91
+
92
+ unlockApp();
93
+ };
94
+
95
+ if (isLoadingRole) {
96
+ return <LoadingIndicatorPage />;
97
+ }
98
+
99
+ return (
100
+ <Main>
101
+ {/* TODO: this needs to be translated */}
102
+ <SettingsPageTitle name="Roles" />
103
+ <Formik
104
+ enableReinitialize
105
+ initialValues={{ name: role.name, description: role.description }}
106
+ onSubmit={handleEditRoleSubmit}
107
+ validationSchema={createRoleSchema}
108
+ >
109
+ {({ handleSubmit, values, handleChange, errors }) => (
110
+ <Form noValidate onSubmit={handleSubmit}>
111
+ <HeaderLayout
112
+ primaryAction={
113
+ !isLoadingPlugins && (
114
+ <Button
115
+ disabled={role.code === 'strapi-super-admin'}
116
+ type="submit"
117
+ loading={mutation.isLoading}
118
+ startIcon={<Check />}
119
+ >
120
+ {formatMessage({
121
+ id: 'global.save',
122
+ defaultMessage: 'Save',
123
+ })}
124
+ </Button>
125
+ )
126
+ }
127
+ title={role.name}
128
+ 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
+ }
137
+ />
138
+ <ContentLayout>
139
+ <Flex
140
+ background="neutral0"
141
+ direction="column"
142
+ alignItems="stretch"
143
+ gap={7}
144
+ hasRadius
145
+ paddingTop={6}
146
+ paddingBottom={6}
147
+ paddingLeft={7}
148
+ paddingRight={7}
149
+ shadow="filterShadow"
150
+ >
151
+ <Flex direction="column" alignItems="stretch" gap={4}>
152
+ <Typography variant="delta" as="h2">
153
+ {formatMessage({
154
+ id: getTrad('EditPage.form.roles'),
155
+ defaultMessage: 'Role details',
156
+ })}
157
+ </Typography>
158
+
159
+ <Grid gap={4}>
160
+ <GridItem col={6}>
161
+ <TextInput
162
+ name="name"
163
+ value={values.name || ''}
164
+ onChange={handleChange}
165
+ label={formatMessage({
166
+ id: 'global.name',
167
+ defaultMessage: 'Name',
168
+ })}
169
+ error={
170
+ errors?.name
171
+ ? formatMessage({ id: errors.name, defaultMessage: 'Name is required' })
172
+ : false
173
+ }
174
+ required
175
+ />
176
+ </GridItem>
177
+ <GridItem col={6}>
178
+ <Textarea
179
+ id="description"
180
+ value={values.description || ''}
181
+ onChange={handleChange}
182
+ label={formatMessage({
183
+ id: 'global.description',
184
+ defaultMessage: 'Description',
185
+ })}
186
+ error={
187
+ errors?.description
188
+ ? formatMessage({
189
+ id: errors.description,
190
+ defaultMessage: 'Description is required',
191
+ })
192
+ : false
193
+ }
194
+ required
195
+ />
196
+ </GridItem>
197
+ </Grid>
198
+ </Flex>
199
+
200
+ {!isLoadingPlugins && (
201
+ <UsersPermissions
202
+ ref={permissionsRef}
203
+ permissions={role.permissions}
204
+ routes={routes}
205
+ />
206
+ )}
207
+ </Flex>
208
+ </ContentLayout>
209
+ </Form>
210
+ )}
211
+ </Formik>
212
+ </Main>
213
+ );
214
+ };
215
+
216
+ export const ProtectedRolesEditPage = () => (
217
+ <CheckPagePermissions permissions={PERMISSIONS.updateRole}>
218
+ <EditPage />
219
+ </CheckPagePermissions>
220
+ );
@@ -1,13 +1,39 @@
1
1
  import React from 'react';
2
2
 
3
- import { Flex, IconButton, Tbody, Td, Tr, Typography } from '@strapi/design-system';
4
- import { CheckPermissions, onRowClick, stopPropagation } from '@strapi/helper-plugin';
3
+ import { Flex, IconButton, Link, Tbody, Td, Tr, Typography } from '@strapi/design-system';
4
+ import { CheckPermissions, onRowClick, pxToRem, 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
8
  import { useHistory } from 'react-router-dom';
9
+ import styled from 'styled-components';
9
10
 
10
- import pluginId from '../../../../pluginId';
11
+ const EditLink = styled(Link)`
12
+ align-items: center;
13
+ height: ${pxToRem(32)};
14
+ display: flex;
15
+ justify-content: center;
16
+ padding: ${({ theme }) => `${theme.spaces[2]}}`};
17
+ width: ${pxToRem(32)};
18
+
19
+ svg {
20
+ height: ${pxToRem(12)};
21
+ width: ${pxToRem(12)};
22
+
23
+ path {
24
+ fill: ${({ theme }) => theme.colors.neutral500};
25
+ }
26
+ }
27
+
28
+ &:hover,
29
+ &:focus {
30
+ svg {
31
+ path {
32
+ fill: ${({ theme }) => theme.colors.neutral800};
33
+ }
34
+ }
35
+ }
36
+ `;
11
37
 
12
38
  const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDelete }) => {
13
39
  const { formatMessage } = useIntl();
@@ -23,7 +49,7 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
23
49
  };
24
50
 
25
51
  const handleClickEdit = (id) => {
26
- push(`/settings/${pluginId}/roles/${id}`);
52
+ push(`/settings/users-permissions/roles/${id}`);
27
53
  };
28
54
 
29
55
  return (
@@ -38,25 +64,29 @@ const TableBody = ({ sortedRoles, canDelete, permissions, setRoleToDelete, onDel
38
64
  </Td>
39
65
  <Td width="30%">
40
66
  <Typography>
41
- {`${role.nb_users} ${formatMessage({
42
- id: 'global.users',
43
- defaultMessage: 'users',
44
- }).toLowerCase()}`}
67
+ {formatMessage(
68
+ {
69
+ id: 'Roles.RoleRow.user-count',
70
+ defaultMessage: '{number, plural, =0 {# user} one {# user} other {# users}}',
71
+ },
72
+ { number: role.nb_users }
73
+ )}
45
74
  </Typography>
46
75
  </Td>
47
76
  <Td>
48
77
  <Flex justifyContent="end" {...stopPropagation}>
49
78
  <CheckPermissions permissions={permissions.updateRole}>
50
- <IconButton
51
- onClick={() => handleClickEdit(role.id)}
52
- noBorder
53
- icon={<Pencil />}
54
- label={formatMessage(
79
+ <EditLink
80
+ to={`/settings/users-permissions/roles/${role.id}`}
81
+ aria-label={formatMessage(
55
82
  { id: 'app.component.table.edit', defaultMessage: 'Edit {target}' },
56
83
  { target: `${role.name}` }
57
84
  )}
58
- />
85
+ >
86
+ <Pencil />
87
+ </EditLink>
59
88
  </CheckPermissions>
89
+
60
90
  {checkCanDeleteRole(role) && (
61
91
  <CheckPermissions permissions={permissions.deleteRole}>
62
92
  <IconButton