@strapi/plugin-users-permissions 5.0.0-beta.6 → 5.0.0-beta.7

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 (144) hide show
  1. package/admin/src/components/BoundRoute/index.jsx +2 -2
  2. package/admin/src/components/FormModal/Input/index.jsx +32 -31
  3. package/admin/src/components/Permissions/PermissionRow/CheckboxWrapper.jsx +1 -1
  4. package/admin/src/components/Permissions/PermissionRow/SubCategory.jsx +2 -2
  5. package/admin/src/components/Policies/index.jsx +2 -2
  6. package/admin/src/components/UsersPermissions/index.jsx +2 -2
  7. package/admin/src/pages/AdvancedSettings/index.jsx +6 -16
  8. package/admin/src/pages/EmailTemplates/components/EmailForm.jsx +2 -1
  9. package/admin/src/pages/EmailTemplates/index.jsx +5 -4
  10. package/admin/src/pages/Providers/index.jsx +6 -9
  11. package/admin/src/pages/Roles/pages/CreatePage.jsx +29 -24
  12. package/admin/src/pages/Roles/pages/EditPage.jsx +33 -24
  13. package/admin/src/pages/Roles/pages/ListPage/components/TableBody.jsx +1 -1
  14. package/admin/src/pages/Roles/pages/ListPage/index.jsx +8 -11
  15. package/dist/_chunks/ar-BguGUqwK.js +44 -0
  16. package/dist/_chunks/ar-BguGUqwK.js.map +1 -0
  17. package/dist/_chunks/ar-CK8BRRXB.mjs +44 -0
  18. package/dist/_chunks/ar-CK8BRRXB.mjs.map +1 -0
  19. package/dist/_chunks/cs-BVigMk0l.mjs +50 -0
  20. package/dist/_chunks/cs-BVigMk0l.mjs.map +1 -0
  21. package/dist/_chunks/cs-BW8-K_GY.js +50 -0
  22. package/dist/_chunks/cs-BW8-K_GY.js.map +1 -0
  23. package/dist/_chunks/de-BKUdRFI4.mjs +62 -0
  24. package/dist/_chunks/de-BKUdRFI4.mjs.map +1 -0
  25. package/dist/_chunks/de-owXpVluI.js +62 -0
  26. package/dist/_chunks/de-owXpVluI.js.map +1 -0
  27. package/dist/_chunks/dk-BQiTK50l.mjs +86 -0
  28. package/dist/_chunks/dk-BQiTK50l.mjs.map +1 -0
  29. package/dist/_chunks/dk-LXAnbuBk.js +86 -0
  30. package/dist/_chunks/dk-LXAnbuBk.js.map +1 -0
  31. package/dist/_chunks/en-DOHtPf-2.mjs +86 -0
  32. package/dist/_chunks/en-DOHtPf-2.mjs.map +1 -0
  33. package/dist/_chunks/en-MHo5mcsU.js +86 -0
  34. package/dist/_chunks/en-MHo5mcsU.js.map +1 -0
  35. package/dist/_chunks/es-BwLCLXAQ.js +86 -0
  36. package/dist/_chunks/es-BwLCLXAQ.js.map +1 -0
  37. package/dist/_chunks/es-DNgOVMjD.mjs +86 -0
  38. package/dist/_chunks/es-DNgOVMjD.mjs.map +1 -0
  39. package/dist/_chunks/fr-DkgRugiU.mjs +50 -0
  40. package/dist/_chunks/fr-DkgRugiU.mjs.map +1 -0
  41. package/dist/_chunks/fr-DkhpSjjm.js +50 -0
  42. package/dist/_chunks/fr-DkhpSjjm.js.map +1 -0
  43. package/dist/_chunks/id-BTemOeTZ.js +62 -0
  44. package/dist/_chunks/id-BTemOeTZ.js.map +1 -0
  45. package/dist/_chunks/id-BdEsvnaF.mjs +62 -0
  46. package/dist/_chunks/id-BdEsvnaF.mjs.map +1 -0
  47. package/dist/_chunks/index--VHJr8__-Cx-PmaTb.mjs +10577 -0
  48. package/dist/_chunks/index--VHJr8__-Cx-PmaTb.mjs.map +1 -0
  49. package/dist/_chunks/index--VHJr8__-a6vFdIyj.js +10601 -0
  50. package/dist/_chunks/index--VHJr8__-a6vFdIyj.js.map +1 -0
  51. package/dist/_chunks/index-B-kWBHFg.mjs +1153 -0
  52. package/dist/_chunks/index-B-kWBHFg.mjs.map +1 -0
  53. package/dist/_chunks/index-B0IECp2U.js +272 -0
  54. package/dist/_chunks/index-B0IECp2U.js.map +1 -0
  55. package/dist/_chunks/index-BEhglMSg.mjs +249 -0
  56. package/dist/_chunks/index-BEhglMSg.mjs.map +1 -0
  57. package/dist/_chunks/index-BRZCpa1U.mjs +335 -0
  58. package/dist/_chunks/index-BRZCpa1U.mjs.map +1 -0
  59. package/dist/_chunks/index-CYVy1i0_.mjs +253 -0
  60. package/dist/_chunks/index-CYVy1i0_.mjs.map +1 -0
  61. package/dist/_chunks/index-CvBfU3H9.js +634 -0
  62. package/dist/_chunks/index-CvBfU3H9.js.map +1 -0
  63. package/dist/_chunks/index-D6s_GV1d.js +357 -0
  64. package/dist/_chunks/index-D6s_GV1d.js.map +1 -0
  65. package/dist/_chunks/index-Du8DLJcX.mjs +611 -0
  66. package/dist/_chunks/index-Du8DLJcX.mjs.map +1 -0
  67. package/dist/_chunks/index-QrGtUO_1.js +248 -0
  68. package/dist/_chunks/index-QrGtUO_1.js.map +1 -0
  69. package/dist/_chunks/index-lnrC9bmZ.js +1183 -0
  70. package/dist/_chunks/index-lnrC9bmZ.js.map +1 -0
  71. package/dist/_chunks/it-B-rv0E24.mjs +62 -0
  72. package/dist/_chunks/it-B-rv0E24.mjs.map +1 -0
  73. package/dist/_chunks/it-D1rH6V6_.js +62 -0
  74. package/dist/_chunks/it-D1rH6V6_.js.map +1 -0
  75. package/dist/_chunks/ja-C8K-VBPD.mjs +48 -0
  76. package/dist/_chunks/ja-C8K-VBPD.mjs.map +1 -0
  77. package/dist/_chunks/ja-DqShgTMf.js +48 -0
  78. package/dist/_chunks/ja-DqShgTMf.js.map +1 -0
  79. package/dist/_chunks/ko-B9DGEPWH.js +86 -0
  80. package/dist/_chunks/ko-B9DGEPWH.js.map +1 -0
  81. package/dist/_chunks/ko-Busb0wIY.mjs +86 -0
  82. package/dist/_chunks/ko-Busb0wIY.mjs.map +1 -0
  83. package/dist/_chunks/ms-ByvsQjRt.mjs +49 -0
  84. package/dist/_chunks/ms-ByvsQjRt.mjs.map +1 -0
  85. package/dist/_chunks/ms-CPBU3LWf.js +49 -0
  86. package/dist/_chunks/ms-CPBU3LWf.js.map +1 -0
  87. package/dist/_chunks/nl-5qO8Rpcy.mjs +48 -0
  88. package/dist/_chunks/nl-5qO8Rpcy.mjs.map +1 -0
  89. package/dist/_chunks/nl-CwNB6YoO.js +48 -0
  90. package/dist/_chunks/nl-CwNB6YoO.js.map +1 -0
  91. package/dist/_chunks/pl-BdIzifBE.mjs +86 -0
  92. package/dist/_chunks/pl-BdIzifBE.mjs.map +1 -0
  93. package/dist/_chunks/pl-Do9UD69f.js +86 -0
  94. package/dist/_chunks/pl-Do9UD69f.js.map +1 -0
  95. package/dist/_chunks/pt-BIO24ioG.mjs +48 -0
  96. package/dist/_chunks/pt-BIO24ioG.mjs.map +1 -0
  97. package/dist/_chunks/pt-BR-D7dZhxuP.js +44 -0
  98. package/dist/_chunks/pt-BR-D7dZhxuP.js.map +1 -0
  99. package/dist/_chunks/pt-BR-f0p23AQZ.mjs +44 -0
  100. package/dist/_chunks/pt-BR-f0p23AQZ.mjs.map +1 -0
  101. package/dist/_chunks/pt-fdvyOnUp.js +48 -0
  102. package/dist/_chunks/pt-fdvyOnUp.js.map +1 -0
  103. package/dist/_chunks/ru-C94rjPGA.js +86 -0
  104. package/dist/_chunks/ru-C94rjPGA.js.map +1 -0
  105. package/dist/_chunks/ru-VWy-IB7K.mjs +86 -0
  106. package/dist/_chunks/ru-VWy-IB7K.mjs.map +1 -0
  107. package/dist/_chunks/sk-BABEhykl.js +50 -0
  108. package/dist/_chunks/sk-BABEhykl.js.map +1 -0
  109. package/dist/_chunks/sk-B_LIcepm.mjs +50 -0
  110. package/dist/_chunks/sk-B_LIcepm.mjs.map +1 -0
  111. package/dist/_chunks/sv-ABLKOokl.mjs +86 -0
  112. package/dist/_chunks/sv-ABLKOokl.mjs.map +1 -0
  113. package/dist/_chunks/sv-Be43LhA9.js +86 -0
  114. package/dist/_chunks/sv-Be43LhA9.js.map +1 -0
  115. package/dist/_chunks/th-DKyP7ueR.mjs +60 -0
  116. package/dist/_chunks/th-DKyP7ueR.mjs.map +1 -0
  117. package/dist/_chunks/th-DgVhVLhL.js +60 -0
  118. package/dist/_chunks/th-DgVhVLhL.js.map +1 -0
  119. package/dist/_chunks/tr-B_idhkEs.js +85 -0
  120. package/dist/_chunks/tr-B_idhkEs.js.map +1 -0
  121. package/dist/_chunks/tr-qa1Q5UjC.mjs +85 -0
  122. package/dist/_chunks/tr-qa1Q5UjC.mjs.map +1 -0
  123. package/dist/_chunks/uk-BmRqbeQc.mjs +49 -0
  124. package/dist/_chunks/uk-BmRqbeQc.mjs.map +1 -0
  125. package/dist/_chunks/uk-LHOivnhP.js +49 -0
  126. package/dist/_chunks/uk-LHOivnhP.js.map +1 -0
  127. package/dist/_chunks/vi-CdVRdKDw.js +50 -0
  128. package/dist/_chunks/vi-CdVRdKDw.js.map +1 -0
  129. package/dist/_chunks/vi-HW-EdMea.mjs +50 -0
  130. package/dist/_chunks/vi-HW-EdMea.mjs.map +1 -0
  131. package/dist/_chunks/zh-5hKkVPA4.mjs +86 -0
  132. package/dist/_chunks/zh-5hKkVPA4.mjs.map +1 -0
  133. package/dist/_chunks/zh-Cuq8gMnF.js +86 -0
  134. package/dist/_chunks/zh-Cuq8gMnF.js.map +1 -0
  135. package/dist/_chunks/zh-Hans-BHilK-yc.mjs +86 -0
  136. package/dist/_chunks/zh-Hans-BHilK-yc.mjs.map +1 -0
  137. package/dist/_chunks/zh-Hans-GQDMKtY4.js +86 -0
  138. package/dist/_chunks/zh-Hans-GQDMKtY4.js.map +1 -0
  139. package/dist/admin/index.js +4 -0
  140. package/dist/admin/index.js.map +1 -0
  141. package/dist/admin/index.mjs +5 -0
  142. package/dist/admin/index.mjs.map +1 -0
  143. package/package.json +8 -8
  144. package/server/utils/sanitize/visitors/remove-user-relation-from-role-entities.js +2 -2
@@ -0,0 +1,1153 @@
1
+ import { jsx, jsxs } from "react/jsx-runtime";
2
+ import * as React from "react";
3
+ import { createContext, useContext, useMemo, useCallback, useReducer, forwardRef, useImperativeHandle, memo, useEffect, useState } from "react";
4
+ import { translatedErrors, useNotification, useFetchClient, useAPIErrorHandler, Page, useTracking, Layouts, BackButton, useQueryParams, useRBAC, SearchInput, ConfirmDialog } from "@strapi/strapi/admin";
5
+ import { useNavigate, useMatch, NavLink, Routes, Route } from "react-router-dom";
6
+ import { g as getTrad, P as PERMISSIONS } from "./index-BEhglMSg.mjs";
7
+ import { Box, Flex, Typography, Checkbox, Grid, GridItem, VisuallyHidden, Accordion, AccordionToggle, AccordionContent, Main, Button, Field, TextInput, Textarea, Link, Tbody, Tr, Td, IconButton, useNotifyAT, useFilter, useCollator, LinkButton, Table, Thead, Th, EmptyStateLayout } from "@strapi/design-system";
8
+ import { Cog, Check, Pencil, Trash, Plus } from "@strapi/icons";
9
+ import { Formik, Form } from "formik";
10
+ import { useIntl } from "react-intl";
11
+ import { useQueries, useMutation, useQuery } from "react-query";
12
+ import PropTypes from "prop-types";
13
+ import upperFirst from "lodash/upperFirst";
14
+ import sortBy from "lodash/sortBy";
15
+ import get from "lodash/get";
16
+ import { css, styled } from "styled-components";
17
+ import { produce } from "immer";
18
+ import isEmpty from "lodash/isEmpty";
19
+ import without from "lodash/without";
20
+ import map from "lodash/map";
21
+ import tail from "lodash/tail";
22
+ import set from "lodash/set";
23
+ import take from "lodash/take";
24
+ import * as yup from "yup";
25
+ const UsersPermissions$2 = createContext({});
26
+ const UsersPermissionsProvider = ({ children, value }) => {
27
+ return /* @__PURE__ */ jsx(UsersPermissions$2.Provider, { value, children });
28
+ };
29
+ const useUsersPermissions = () => useContext(UsersPermissions$2);
30
+ UsersPermissionsProvider.propTypes = {
31
+ children: PropTypes.node.isRequired,
32
+ value: PropTypes.object.isRequired
33
+ };
34
+ function formatPluginName(pluginSlug) {
35
+ switch (pluginSlug) {
36
+ case "application":
37
+ return "Application";
38
+ case "plugin::content-manager":
39
+ return "Content manager";
40
+ case "plugin::content-type-builder":
41
+ return "Content types builder";
42
+ case "plugin::documentation":
43
+ return "Documentation";
44
+ case "plugin::email":
45
+ return "Email";
46
+ case "plugin::i18n":
47
+ return "i18n";
48
+ case "plugin::upload":
49
+ return "Upload";
50
+ case "plugin::users-permissions":
51
+ return "Users-permissions";
52
+ default:
53
+ return upperFirst(pluginSlug.replace("api::", "").replace("plugin::", ""));
54
+ }
55
+ }
56
+ const init$1 = (initialState2, permissions) => {
57
+ const collapses = Object.keys(permissions).sort().map((name) => ({ name, isOpen: false }));
58
+ return { ...initialState2, collapses };
59
+ };
60
+ const activeCheckboxWrapperStyles = css`
61
+ background: ${(props) => props.theme.colors.primary100};
62
+ svg {
63
+ opacity: 1;
64
+ }
65
+ `;
66
+ const CheckboxWrapper = styled(Box)`
67
+ display: flex;
68
+ justify-content: space-between;
69
+ align-items: center;
70
+
71
+ svg {
72
+ opacity: 0;
73
+ path {
74
+ fill: ${(props) => props.theme.colors.primary600};
75
+ }
76
+ }
77
+
78
+ /* Show active style both on hover and when the action is selected */
79
+ ${(props) => props.isActive && activeCheckboxWrapperStyles}
80
+ &:hover {
81
+ ${activeCheckboxWrapperStyles}
82
+ }
83
+ `;
84
+ const Border = styled.div`
85
+ flex: 1;
86
+ align-self: center;
87
+ border-top: 1px solid ${({ theme }) => theme.colors.neutral150};
88
+ `;
89
+ const SubCategory = ({ subCategory }) => {
90
+ const { formatMessage } = useIntl();
91
+ const { onChange, onChangeSelectAll, onSelectedAction, selectedAction, modifiedData } = useUsersPermissions();
92
+ const currentScopedModifiedData = useMemo(() => {
93
+ return get(modifiedData, subCategory.name, {});
94
+ }, [modifiedData, subCategory]);
95
+ const hasAllActionsSelected = useMemo(() => {
96
+ return Object.values(currentScopedModifiedData).every((action) => action.enabled === true);
97
+ }, [currentScopedModifiedData]);
98
+ const hasSomeActionsSelected = useMemo(() => {
99
+ return Object.values(currentScopedModifiedData).some((action) => action.enabled === true) && !hasAllActionsSelected;
100
+ }, [currentScopedModifiedData, hasAllActionsSelected]);
101
+ const handleChangeSelectAll = useCallback(
102
+ ({ target: { name } }) => {
103
+ onChangeSelectAll({ target: { name, value: !hasAllActionsSelected } });
104
+ },
105
+ [hasAllActionsSelected, onChangeSelectAll]
106
+ );
107
+ const isActionSelected = useCallback(
108
+ (actionName) => {
109
+ return selectedAction === actionName;
110
+ },
111
+ [selectedAction]
112
+ );
113
+ return /* @__PURE__ */ jsxs(Box, { children: [
114
+ /* @__PURE__ */ jsxs(Flex, { justifyContent: "space-between", alignItems: "center", children: [
115
+ /* @__PURE__ */ jsx(Box, { paddingRight: 4, children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: subCategory.label }) }),
116
+ /* @__PURE__ */ jsx(Border, {}),
117
+ /* @__PURE__ */ jsx(Box, { paddingLeft: 4, children: /* @__PURE__ */ jsx(
118
+ Checkbox,
119
+ {
120
+ name: subCategory.name,
121
+ value: hasAllActionsSelected,
122
+ onValueChange: (value) => handleChangeSelectAll({ target: { name: subCategory.name, value } }),
123
+ indeterminate: hasSomeActionsSelected,
124
+ children: formatMessage({ id: "app.utils.select-all", defaultMessage: "Select all" })
125
+ }
126
+ ) })
127
+ ] }),
128
+ /* @__PURE__ */ jsx(Flex, { paddingTop: 6, paddingBottom: 6, children: /* @__PURE__ */ jsx(Grid, { gap: 2, style: { flex: 1 }, children: subCategory.actions.map((action) => {
129
+ const name = `${action.name}.enabled`;
130
+ return /* @__PURE__ */ jsx(GridItem, { col: 6, children: /* @__PURE__ */ jsxs(CheckboxWrapper, { isActive: isActionSelected(action.name), padding: 2, hasRadius: true, children: [
131
+ /* @__PURE__ */ jsx(
132
+ Checkbox,
133
+ {
134
+ value: get(modifiedData, name, false),
135
+ name,
136
+ onValueChange: (value) => onChange({ target: { name, value } }),
137
+ children: action.label
138
+ }
139
+ ),
140
+ /* @__PURE__ */ jsxs(
141
+ "button",
142
+ {
143
+ type: "button",
144
+ onClick: () => onSelectedAction(action.name),
145
+ style: { display: "inline-flex", alignItems: "center" },
146
+ children: [
147
+ /* @__PURE__ */ jsx(VisuallyHidden, { tag: "span", children: formatMessage(
148
+ {
149
+ id: "app.utils.show-bound-route",
150
+ defaultMessage: "Show bound route for {route}"
151
+ },
152
+ {
153
+ route: action.name
154
+ }
155
+ ) }),
156
+ /* @__PURE__ */ jsx(Cog, {})
157
+ ]
158
+ }
159
+ )
160
+ ] }) }, action.name);
161
+ }) }) })
162
+ ] });
163
+ };
164
+ SubCategory.propTypes = {
165
+ subCategory: PropTypes.object.isRequired
166
+ };
167
+ const PermissionRow = ({ name, permissions }) => {
168
+ const subCategories = useMemo(() => {
169
+ return sortBy(
170
+ Object.values(permissions.controllers).reduce((acc, curr, index) => {
171
+ const currentName = `${name}.controllers.${Object.keys(permissions.controllers)[index]}`;
172
+ const actions = sortBy(
173
+ Object.keys(curr).reduce((acc2, current) => {
174
+ return [
175
+ ...acc2,
176
+ {
177
+ ...curr[current],
178
+ label: current,
179
+ name: `${currentName}.${current}`
180
+ }
181
+ ];
182
+ }, []),
183
+ "label"
184
+ );
185
+ return [
186
+ ...acc,
187
+ {
188
+ actions,
189
+ label: Object.keys(permissions.controllers)[index],
190
+ name: currentName
191
+ }
192
+ ];
193
+ }, []),
194
+ "label"
195
+ );
196
+ }, [name, permissions]);
197
+ return /* @__PURE__ */ jsx(Box, { padding: 6, children: subCategories.map((subCategory) => /* @__PURE__ */ jsx(SubCategory, { subCategory }, subCategory.name)) });
198
+ };
199
+ PermissionRow.propTypes = {
200
+ name: PropTypes.string.isRequired,
201
+ permissions: PropTypes.object.isRequired
202
+ };
203
+ const initialState$1 = {
204
+ collapses: []
205
+ };
206
+ const reducer$1 = (state, action) => (
207
+ // eslint-disable-next-line consistent-return
208
+ produce(state, (draftState) => {
209
+ switch (action.type) {
210
+ case "TOGGLE_COLLAPSE": {
211
+ draftState.collapses = state.collapses.map((collapse, index) => {
212
+ if (index === action.index) {
213
+ return { ...collapse, isOpen: !collapse.isOpen };
214
+ }
215
+ return { ...collapse, isOpen: false };
216
+ });
217
+ break;
218
+ }
219
+ default:
220
+ return draftState;
221
+ }
222
+ })
223
+ );
224
+ const Permissions = () => {
225
+ const { modifiedData } = useUsersPermissions();
226
+ const { formatMessage } = useIntl();
227
+ const [{ collapses }, dispatch] = useReducer(
228
+ reducer$1,
229
+ initialState$1,
230
+ (state) => init$1(state, modifiedData)
231
+ );
232
+ const handleToggle = (index) => dispatch({
233
+ type: "TOGGLE_COLLAPSE",
234
+ index
235
+ });
236
+ return /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 1, children: collapses.map((collapse, index) => /* @__PURE__ */ jsxs(
237
+ Accordion,
238
+ {
239
+ expanded: collapse.isOpen,
240
+ onToggle: () => handleToggle(index),
241
+ variant: index % 2 === 0 ? "secondary" : void 0,
242
+ children: [
243
+ /* @__PURE__ */ jsx(
244
+ AccordionToggle,
245
+ {
246
+ title: formatPluginName(collapse.name),
247
+ description: formatMessage(
248
+ {
249
+ id: "users-permissions.Plugin.permissions.plugins.description",
250
+ defaultMessage: "Define all allowed actions for the {name} plugin."
251
+ },
252
+ { name: collapse.name }
253
+ ),
254
+ variant: index % 2 ? "primary" : "secondary"
255
+ }
256
+ ),
257
+ /* @__PURE__ */ jsx(AccordionContent, { children: /* @__PURE__ */ jsx(PermissionRow, { permissions: modifiedData[collapse.name], name: collapse.name }) })
258
+ ]
259
+ },
260
+ collapse.name
261
+ )) });
262
+ };
263
+ const getMethodColor = (verb) => {
264
+ switch (verb) {
265
+ case "POST": {
266
+ return {
267
+ text: "success600",
268
+ border: "success200",
269
+ background: "success100"
270
+ };
271
+ }
272
+ case "GET": {
273
+ return {
274
+ text: "secondary600",
275
+ border: "secondary200",
276
+ background: "secondary100"
277
+ };
278
+ }
279
+ case "PUT": {
280
+ return {
281
+ text: "warning600",
282
+ border: "warning200",
283
+ background: "warning100"
284
+ };
285
+ }
286
+ case "DELETE": {
287
+ return {
288
+ text: "danger600",
289
+ border: "danger200",
290
+ background: "danger100"
291
+ };
292
+ }
293
+ default: {
294
+ return {
295
+ text: "neutral600",
296
+ border: "neutral200",
297
+ background: "neutral100"
298
+ };
299
+ }
300
+ }
301
+ };
302
+ const MethodBox = styled(Box)`
303
+ margin: -1px;
304
+ border-radius: ${({ theme }) => theme.spaces[1]} 0 0 ${({ theme }) => theme.spaces[1]};
305
+ `;
306
+ function BoundRoute({ route }) {
307
+ const { formatMessage } = useIntl();
308
+ const { method, handler: title, path } = route;
309
+ const formattedRoute = path ? tail(path.split("/")) : [];
310
+ const [controller = "", action = ""] = title ? title.split(".") : [];
311
+ const colors = getMethodColor(route.method);
312
+ return /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
313
+ /* @__PURE__ */ jsxs(Typography, { variant: "delta", tag: "h3", children: [
314
+ formatMessage({
315
+ id: "users-permissions.BoundRoute.title",
316
+ defaultMessage: "Bound route to"
317
+ }),
318
+ " ",
319
+ /* @__PURE__ */ jsx("span", { children: controller }),
320
+ /* @__PURE__ */ jsxs(Typography, { variant: "delta", textColor: "primary600", children: [
321
+ ".",
322
+ action
323
+ ] })
324
+ ] }),
325
+ /* @__PURE__ */ jsxs(Flex, { hasRadius: true, background: "neutral0", borderColor: "neutral200", gap: 0, children: [
326
+ /* @__PURE__ */ jsx(MethodBox, { background: colors.background, borderColor: colors.border, padding: 2, children: /* @__PURE__ */ jsx(Typography, { fontWeight: "bold", textColor: colors.text, children: method }) }),
327
+ /* @__PURE__ */ jsx(Box, { paddingLeft: 2, paddingRight: 2, children: map(formattedRoute, (value) => /* @__PURE__ */ jsxs(Typography, { textColor: value.includes(":") ? "neutral600" : "neutral900", children: [
328
+ "/",
329
+ value
330
+ ] }, value)) })
331
+ ] })
332
+ ] });
333
+ }
334
+ BoundRoute.defaultProps = {
335
+ route: {
336
+ handler: "Nocontroller.error",
337
+ method: "GET",
338
+ path: "/there-is-no-path"
339
+ }
340
+ };
341
+ BoundRoute.propTypes = {
342
+ route: PropTypes.shape({
343
+ handler: PropTypes.string,
344
+ method: PropTypes.string,
345
+ path: PropTypes.string
346
+ })
347
+ };
348
+ const Policies = () => {
349
+ const { formatMessage } = useIntl();
350
+ const { selectedAction, routes } = useUsersPermissions();
351
+ const path = without(selectedAction.split("."), "controllers");
352
+ const controllerRoutes = get(routes, path[0]);
353
+ const pathResolved = path.slice(1).join(".");
354
+ const displayedRoutes = isEmpty(controllerRoutes) ? [] : controllerRoutes.filter((o) => o.handler.endsWith(pathResolved));
355
+ return /* @__PURE__ */ jsx(
356
+ GridItem,
357
+ {
358
+ col: 5,
359
+ background: "neutral150",
360
+ paddingTop: 6,
361
+ paddingBottom: 6,
362
+ paddingLeft: 7,
363
+ paddingRight: 7,
364
+ style: { minHeight: "100%" },
365
+ children: selectedAction ? /* @__PURE__ */ jsx(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: displayedRoutes.map((route, key) => (
366
+ // eslint-disable-next-line react/no-array-index-key
367
+ /* @__PURE__ */ jsx(BoundRoute, { route }, key)
368
+ )) }) : /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
369
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h3", children: formatMessage({
370
+ id: "users-permissions.Policies.header.title",
371
+ defaultMessage: "Advanced settings"
372
+ }) }),
373
+ /* @__PURE__ */ jsx(Typography, { tag: "p", textColor: "neutral600", children: formatMessage({
374
+ id: "users-permissions.Policies.header.hint",
375
+ defaultMessage: "Select the application's actions or the plugin's actions and click on the cog icon to display the bound route"
376
+ }) })
377
+ ] })
378
+ }
379
+ );
380
+ };
381
+ const init = (state, permissions, routes) => {
382
+ return {
383
+ ...state,
384
+ initialData: permissions,
385
+ modifiedData: permissions,
386
+ routes
387
+ };
388
+ };
389
+ const initialState = {
390
+ initialData: {},
391
+ modifiedData: {},
392
+ routes: {},
393
+ selectedAction: "",
394
+ policies: []
395
+ };
396
+ const reducer = (state, action) => produce(state, (draftState) => {
397
+ switch (action.type) {
398
+ case "ON_CHANGE": {
399
+ const keysLength = action.keys.length;
400
+ const isChangingCheckbox = action.keys[keysLength - 1] === "enabled";
401
+ if (action.value && isChangingCheckbox) {
402
+ const selectedAction = take(action.keys, keysLength - 1).join(".");
403
+ draftState.selectedAction = selectedAction;
404
+ }
405
+ set(draftState, ["modifiedData", ...action.keys], action.value);
406
+ break;
407
+ }
408
+ case "ON_CHANGE_SELECT_ALL": {
409
+ const pathToValue = ["modifiedData", ...action.keys];
410
+ const oldValues = get(state, pathToValue, {});
411
+ const updatedValues = Object.keys(oldValues).reduce((acc, current) => {
412
+ acc[current] = { ...oldValues[current], enabled: action.value };
413
+ return acc;
414
+ }, {});
415
+ set(draftState, pathToValue, updatedValues);
416
+ break;
417
+ }
418
+ case "ON_RESET": {
419
+ draftState.modifiedData = state.initialData;
420
+ break;
421
+ }
422
+ case "ON_SUBMIT_SUCCEEDED": {
423
+ draftState.initialData = state.modifiedData;
424
+ break;
425
+ }
426
+ case "SELECT_ACTION": {
427
+ const { actionToSelect } = action;
428
+ draftState.selectedAction = actionToSelect === state.selectedAction ? "" : actionToSelect;
429
+ break;
430
+ }
431
+ default:
432
+ return draftState;
433
+ }
434
+ });
435
+ const UsersPermissions = forwardRef(({ permissions, routes }, ref) => {
436
+ const { formatMessage } = useIntl();
437
+ const [state, dispatch] = useReducer(
438
+ reducer,
439
+ initialState,
440
+ (state2) => init(state2, permissions, routes)
441
+ );
442
+ useImperativeHandle(ref, () => ({
443
+ getPermissions() {
444
+ return {
445
+ permissions: state.modifiedData
446
+ };
447
+ },
448
+ resetForm() {
449
+ dispatch({ type: "ON_RESET" });
450
+ },
451
+ setFormAfterSubmit() {
452
+ dispatch({ type: "ON_SUBMIT_SUCCEEDED" });
453
+ }
454
+ }));
455
+ const handleChange = ({ target: { name, value } }) => dispatch({
456
+ type: "ON_CHANGE",
457
+ keys: name.split("."),
458
+ value: value === "empty__string_value" ? "" : value
459
+ });
460
+ const handleChangeSelectAll = ({ target: { name, value } }) => dispatch({
461
+ type: "ON_CHANGE_SELECT_ALL",
462
+ keys: name.split("."),
463
+ value
464
+ });
465
+ const handleSelectedAction = (actionToSelect) => dispatch({
466
+ type: "SELECT_ACTION",
467
+ actionToSelect
468
+ });
469
+ const providerValue = {
470
+ ...state,
471
+ onChange: handleChange,
472
+ onChangeSelectAll: handleChangeSelectAll,
473
+ onSelectedAction: handleSelectedAction
474
+ };
475
+ return /* @__PURE__ */ jsx(UsersPermissionsProvider, { value: providerValue, children: /* @__PURE__ */ jsxs(Grid, { gap: 0, shadow: "filterShadow", hasRadius: true, background: "neutral0", children: [
476
+ /* @__PURE__ */ jsx(GridItem, { col: 7, paddingTop: 6, paddingBottom: 6, paddingLeft: 7, paddingRight: 7, children: /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 6, children: [
477
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 2, children: [
478
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", children: formatMessage({
479
+ id: getTrad("Plugins.header.title"),
480
+ defaultMessage: "Permissions"
481
+ }) }),
482
+ /* @__PURE__ */ jsx(Typography, { tag: "p", textColor: "neutral600", children: formatMessage({
483
+ id: getTrad("Plugins.header.description"),
484
+ defaultMessage: "Only actions bound by a route are listed below."
485
+ }) })
486
+ ] }),
487
+ /* @__PURE__ */ jsx(Permissions, {})
488
+ ] }) }),
489
+ /* @__PURE__ */ jsx(Policies, {})
490
+ ] }) });
491
+ });
492
+ UsersPermissions.propTypes = {
493
+ permissions: PropTypes.object.isRequired,
494
+ routes: PropTypes.object.isRequired
495
+ };
496
+ const UsersPermissions$1 = memo(UsersPermissions);
497
+ const createRoleSchema = yup.object().shape({
498
+ name: yup.string().required(translatedErrors.required.id),
499
+ description: yup.string().required(translatedErrors.required.id)
500
+ });
501
+ const cleanPermissions = (permissions) => Object.keys(permissions).reduce((acc, current) => {
502
+ const currentPermission = permissions[current].controllers;
503
+ const cleanedControllers = Object.keys(currentPermission).reduce((acc2, curr) => {
504
+ if (isEmpty(currentPermission[curr])) {
505
+ return acc2;
506
+ }
507
+ acc2[curr] = currentPermission[curr];
508
+ return acc2;
509
+ }, {});
510
+ if (isEmpty(cleanedControllers)) {
511
+ return acc;
512
+ }
513
+ acc[current] = { controllers: cleanedControllers };
514
+ return acc;
515
+ }, {});
516
+ const usePlugins = () => {
517
+ const { toggleNotification } = useNotification();
518
+ const { get: get2 } = useFetchClient();
519
+ const { formatAPIError } = useAPIErrorHandler(getTrad);
520
+ const [
521
+ {
522
+ data: permissions,
523
+ isLoading: isLoadingPermissions,
524
+ error: permissionsError,
525
+ refetch: refetchPermissions
526
+ },
527
+ { data: routes, isLoading: isLoadingRoutes, error: routesError, refetch: refetchRoutes }
528
+ ] = useQueries([
529
+ {
530
+ queryKey: ["users-permissions", "permissions"],
531
+ async queryFn() {
532
+ const {
533
+ data: { permissions: permissions2 }
534
+ } = await get2(`/users-permissions/permissions`);
535
+ return permissions2;
536
+ }
537
+ },
538
+ {
539
+ queryKey: ["users-permissions", "routes"],
540
+ async queryFn() {
541
+ const {
542
+ data: { routes: routes2 }
543
+ } = await get2(`/users-permissions/routes`);
544
+ return routes2;
545
+ }
546
+ }
547
+ ]);
548
+ const refetchQueries = async () => {
549
+ await Promise.all([refetchPermissions(), refetchRoutes()]);
550
+ };
551
+ useEffect(() => {
552
+ if (permissionsError) {
553
+ toggleNotification({
554
+ type: "danger",
555
+ message: formatAPIError(permissionsError)
556
+ });
557
+ }
558
+ }, [toggleNotification, permissionsError, formatAPIError]);
559
+ useEffect(() => {
560
+ if (routesError) {
561
+ toggleNotification({
562
+ type: "danger",
563
+ message: formatAPIError(routesError)
564
+ });
565
+ }
566
+ }, [toggleNotification, routesError, formatAPIError]);
567
+ const isLoading = isLoadingPermissions || isLoadingRoutes;
568
+ return {
569
+ // TODO: these return values need to be memoized, otherwise
570
+ // they will create infinite rendering loops when used as
571
+ // effect dependencies
572
+ permissions: permissions ? cleanPermissions(permissions) : {},
573
+ routes: routes ?? {},
574
+ getData: refetchQueries,
575
+ isLoading
576
+ };
577
+ };
578
+ const CreatePage = () => {
579
+ const { formatMessage } = useIntl();
580
+ const { toggleNotification } = useNotification();
581
+ const navigate = useNavigate();
582
+ const { isLoading: isLoadingPlugins, permissions, routes } = usePlugins();
583
+ const { trackUsage } = useTracking();
584
+ const permissionsRef = React.useRef();
585
+ const { post } = useFetchClient();
586
+ const mutation = useMutation((body) => post(`/users-permissions/roles`, body), {
587
+ onError() {
588
+ toggleNotification({
589
+ type: "danger",
590
+ message: formatMessage({
591
+ id: "notification.error",
592
+ defaultMessage: "An error occurred"
593
+ })
594
+ });
595
+ },
596
+ onSuccess() {
597
+ trackUsage("didCreateRole");
598
+ toggleNotification({
599
+ type: "success",
600
+ message: formatMessage({
601
+ id: getTrad("Settings.roles.created"),
602
+ defaultMessage: "Role created"
603
+ })
604
+ });
605
+ navigate(-1);
606
+ }
607
+ });
608
+ const handleCreateRoleSubmit = async (data) => {
609
+ const permissions2 = permissionsRef.current.getPermissions();
610
+ await mutation.mutate({ ...data, ...permissions2, users: [] });
611
+ };
612
+ return /* @__PURE__ */ jsxs(Main, { children: [
613
+ /* @__PURE__ */ jsx(Page.Title, { children: formatMessage(
614
+ { id: "Settings.PageTitle", defaultMessage: "Settings - {name}" },
615
+ { name: "Roles" }
616
+ ) }),
617
+ /* @__PURE__ */ jsx(
618
+ Formik,
619
+ {
620
+ enableReinitialize: true,
621
+ initialValues: { name: "", description: "" },
622
+ onSubmit: handleCreateRoleSubmit,
623
+ validationSchema: createRoleSchema,
624
+ children: ({ handleSubmit, values, handleChange, errors }) => /* @__PURE__ */ jsxs(Form, { noValidate: true, onSubmit: handleSubmit, children: [
625
+ /* @__PURE__ */ jsx(
626
+ Layouts.Header,
627
+ {
628
+ primaryAction: !isLoadingPlugins && /* @__PURE__ */ jsx(Button, { type: "submit", loading: mutation.isLoading, startIcon: /* @__PURE__ */ jsx(Check, {}), children: formatMessage({
629
+ id: "global.save",
630
+ defaultMessage: "Save"
631
+ }) }),
632
+ title: formatMessage({
633
+ id: "Settings.roles.create.title",
634
+ defaultMessage: "Create a role"
635
+ }),
636
+ subtitle: formatMessage({
637
+ id: "Settings.roles.create.description",
638
+ defaultMessage: "Define the rights given to the role"
639
+ })
640
+ }
641
+ ),
642
+ /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsxs(
643
+ Flex,
644
+ {
645
+ background: "neutral0",
646
+ direction: "column",
647
+ alignItems: "stretch",
648
+ gap: 7,
649
+ hasRadius: true,
650
+ paddingTop: 6,
651
+ paddingBottom: 6,
652
+ paddingLeft: 7,
653
+ paddingRight: 7,
654
+ shadow: "filterShadow",
655
+ children: [
656
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", children: [
657
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", children: formatMessage({
658
+ id: getTrad("EditPage.form.roles"),
659
+ defaultMessage: "Role details"
660
+ }) }),
661
+ /* @__PURE__ */ jsxs(Grid, { gap: 4, children: [
662
+ /* @__PURE__ */ jsx(GridItem, { col: 6, children: /* @__PURE__ */ jsxs(
663
+ Field.Root,
664
+ {
665
+ name: "name",
666
+ error: errors?.name ? formatMessage({ id: errors.name, defaultMessage: "Name is required" }) : false,
667
+ required: true,
668
+ children: [
669
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
670
+ id: "global.name",
671
+ defaultMessage: "Name"
672
+ }) }),
673
+ /* @__PURE__ */ jsx(TextInput, { value: values.name || "", onChange: handleChange }),
674
+ /* @__PURE__ */ jsx(Field.Error, {})
675
+ ]
676
+ }
677
+ ) }),
678
+ /* @__PURE__ */ jsx(GridItem, { col: 6, children: /* @__PURE__ */ jsxs(
679
+ Field.Root,
680
+ {
681
+ name: "description",
682
+ error: errors?.description ? formatMessage({
683
+ id: errors.description,
684
+ defaultMessage: "Description is required"
685
+ }) : false,
686
+ required: true,
687
+ children: [
688
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
689
+ id: "global.description",
690
+ defaultMessage: "Description"
691
+ }) }),
692
+ /* @__PURE__ */ jsx(Textarea, { value: values.description || "", onChange: handleChange }),
693
+ /* @__PURE__ */ jsx(Field.Error, {})
694
+ ]
695
+ }
696
+ ) })
697
+ ] })
698
+ ] }),
699
+ !isLoadingPlugins && /* @__PURE__ */ jsx(
700
+ UsersPermissions$1,
701
+ {
702
+ ref: permissionsRef,
703
+ permissions,
704
+ routes
705
+ }
706
+ )
707
+ ]
708
+ }
709
+ ) })
710
+ ] })
711
+ }
712
+ )
713
+ ] });
714
+ };
715
+ const ProtectedRolesCreatePage = () => /* @__PURE__ */ jsx(Page.Protect, { permissions: PERMISSIONS.createRole, children: /* @__PURE__ */ jsx(CreatePage, {}) });
716
+ const EditPage = () => {
717
+ const { formatMessage } = useIntl();
718
+ const { toggleNotification } = useNotification();
719
+ const {
720
+ params: { id }
721
+ } = useMatch(`/settings/users-permissions/roles/:id`);
722
+ const { get: get2 } = useFetchClient();
723
+ const { isLoading: isLoadingPlugins, routes } = usePlugins();
724
+ const {
725
+ data: role,
726
+ isLoading: isLoadingRole,
727
+ refetch: refetchRole
728
+ } = useQuery(["users-permissions", "role", id], async () => {
729
+ const {
730
+ data: { role: role2 }
731
+ } = await get2(`/users-permissions/roles/${id}`);
732
+ return role2;
733
+ });
734
+ const permissionsRef = React.useRef();
735
+ const { put } = useFetchClient();
736
+ const { formatAPIError } = useAPIErrorHandler();
737
+ const mutation = useMutation((body) => put(`/users-permissions/roles/${id}`, body), {
738
+ onError(error) {
739
+ toggleNotification({
740
+ type: "danger",
741
+ message: formatAPIError(error)
742
+ });
743
+ },
744
+ async onSuccess() {
745
+ toggleNotification({
746
+ type: "success",
747
+ message: formatMessage({
748
+ id: getTrad("Settings.roles.created"),
749
+ defaultMessage: "Role edited"
750
+ })
751
+ });
752
+ await refetchRole();
753
+ }
754
+ });
755
+ const handleEditRoleSubmit = async (data) => {
756
+ const permissions = permissionsRef.current.getPermissions();
757
+ await mutation.mutate({ ...data, ...permissions, users: [] });
758
+ };
759
+ if (isLoadingRole) {
760
+ return /* @__PURE__ */ jsx(Page.Loading, {});
761
+ }
762
+ return /* @__PURE__ */ jsxs(Main, { children: [
763
+ /* @__PURE__ */ jsx(Page.Title, { children: formatMessage(
764
+ { id: "Settings.PageTitle", defaultMessage: "Settings - {name}" },
765
+ { name: "Roles" }
766
+ ) }),
767
+ /* @__PURE__ */ jsx(
768
+ Formik,
769
+ {
770
+ enableReinitialize: true,
771
+ initialValues: { name: role.name, description: role.description },
772
+ onSubmit: handleEditRoleSubmit,
773
+ validationSchema: createRoleSchema,
774
+ children: ({ handleSubmit, values, handleChange, errors }) => /* @__PURE__ */ jsxs(Form, { noValidate: true, onSubmit: handleSubmit, children: [
775
+ /* @__PURE__ */ jsx(
776
+ Layouts.Header,
777
+ {
778
+ primaryAction: !isLoadingPlugins ? /* @__PURE__ */ jsx(
779
+ Button,
780
+ {
781
+ disabled: role.code === "strapi-super-admin",
782
+ type: "submit",
783
+ loading: mutation.isLoading,
784
+ startIcon: /* @__PURE__ */ jsx(Check, {}),
785
+ children: formatMessage({
786
+ id: "global.save",
787
+ defaultMessage: "Save"
788
+ })
789
+ }
790
+ ) : null,
791
+ title: role.name,
792
+ subtitle: role.description,
793
+ navigationAction: /* @__PURE__ */ jsx(BackButton, {})
794
+ }
795
+ ),
796
+ /* @__PURE__ */ jsx(Layouts.Content, { children: /* @__PURE__ */ jsxs(
797
+ Flex,
798
+ {
799
+ background: "neutral0",
800
+ direction: "column",
801
+ alignItems: "stretch",
802
+ gap: 7,
803
+ hasRadius: true,
804
+ paddingTop: 6,
805
+ paddingBottom: 6,
806
+ paddingLeft: 7,
807
+ paddingRight: 7,
808
+ shadow: "filterShadow",
809
+ children: [
810
+ /* @__PURE__ */ jsxs(Flex, { direction: "column", alignItems: "stretch", gap: 4, children: [
811
+ /* @__PURE__ */ jsx(Typography, { variant: "delta", tag: "h2", children: formatMessage({
812
+ id: getTrad("EditPage.form.roles"),
813
+ defaultMessage: "Role details"
814
+ }) }),
815
+ /* @__PURE__ */ jsxs(Grid, { gap: 4, children: [
816
+ /* @__PURE__ */ jsx(GridItem, { col: 6, children: /* @__PURE__ */ jsxs(
817
+ Field.Root,
818
+ {
819
+ name: "name",
820
+ error: errors?.name ? formatMessage({
821
+ id: errors.name,
822
+ defaultMessage: "Name is required"
823
+ }) : false,
824
+ required: true,
825
+ children: [
826
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
827
+ id: "global.name",
828
+ defaultMessage: "Name"
829
+ }) }),
830
+ /* @__PURE__ */ jsx(TextInput, { value: values.name || "", onChange: handleChange }),
831
+ /* @__PURE__ */ jsx(Field.Error, {})
832
+ ]
833
+ }
834
+ ) }),
835
+ /* @__PURE__ */ jsx(GridItem, { col: 6, children: /* @__PURE__ */ jsxs(
836
+ Field.Root,
837
+ {
838
+ name: "description",
839
+ error: errors?.description ? formatMessage({
840
+ id: errors.description,
841
+ defaultMessage: "Description is required"
842
+ }) : false,
843
+ required: true,
844
+ children: [
845
+ /* @__PURE__ */ jsx(Field.Label, { children: formatMessage({
846
+ id: "global.description",
847
+ defaultMessage: "Description"
848
+ }) }),
849
+ /* @__PURE__ */ jsx(Textarea, { value: values.description || "", onChange: handleChange }),
850
+ /* @__PURE__ */ jsx(Field.Error, {})
851
+ ]
852
+ }
853
+ ) })
854
+ ] })
855
+ ] }),
856
+ !isLoadingPlugins && /* @__PURE__ */ jsx(
857
+ UsersPermissions$1,
858
+ {
859
+ ref: permissionsRef,
860
+ permissions: role.permissions,
861
+ routes
862
+ }
863
+ )
864
+ ]
865
+ }
866
+ ) })
867
+ ] })
868
+ }
869
+ )
870
+ ] });
871
+ };
872
+ const ProtectedRolesEditPage = () => /* @__PURE__ */ jsx(Page.Protect, { permissions: PERMISSIONS.updateRole, children: /* @__PURE__ */ jsx(EditPage, {}) });
873
+ const EditLink = styled(Link)`
874
+ align-items: center;
875
+ height: 3.2rem;
876
+ display: flex;
877
+ justify-content: center;
878
+ padding: ${({ theme }) => `${theme.spaces[2]}}`};
879
+ width: 3.2rem;
880
+
881
+ svg {
882
+ height: 1.2rem;
883
+ width: 1.2rem;
884
+
885
+ path {
886
+ fill: ${({ theme }) => theme.colors.neutral500};
887
+ }
888
+ }
889
+
890
+ &:hover,
891
+ &:focus {
892
+ svg {
893
+ path {
894
+ fill: ${({ theme }) => theme.colors.neutral800};
895
+ }
896
+ }
897
+ }
898
+ `;
899
+ const TableBody = ({ sortedRoles, canDelete, canUpdate, setRoleToDelete, onDelete }) => {
900
+ const { formatMessage } = useIntl();
901
+ const navigate = useNavigate();
902
+ const [showConfirmDelete, setShowConfirmDelete] = onDelete;
903
+ const checkCanDeleteRole = (role) => canDelete && !["public", "authenticated"].includes(role.type);
904
+ const handleClickDelete = (id) => {
905
+ setRoleToDelete(id);
906
+ setShowConfirmDelete(!showConfirmDelete);
907
+ };
908
+ return /* @__PURE__ */ jsx(Tbody, { children: sortedRoles?.map((role) => /* @__PURE__ */ jsxs(Tr, { onClick: () => navigate(role.id.toString()), children: [
909
+ /* @__PURE__ */ jsx(Td, { width: "20%", children: /* @__PURE__ */ jsx(Typography, { children: role.name }) }),
910
+ /* @__PURE__ */ jsx(Td, { width: "50%", children: /* @__PURE__ */ jsx(Typography, { children: role.description }) }),
911
+ /* @__PURE__ */ jsx(Td, { width: "30%", children: /* @__PURE__ */ jsx(Typography, { children: formatMessage(
912
+ {
913
+ id: "Roles.RoleRow.user-count",
914
+ defaultMessage: "{number, plural, =0 {# user} one {# user} other {# users}}"
915
+ },
916
+ { number: role.nb_users }
917
+ ) }) }),
918
+ /* @__PURE__ */ jsx(Td, { children: /* @__PURE__ */ jsxs(Flex, { justifyContent: "end", onClick: (e) => e.stopPropagation(), children: [
919
+ canUpdate ? /* @__PURE__ */ jsx(
920
+ EditLink,
921
+ {
922
+ to: role.id.toString(),
923
+ "aria-label": formatMessage(
924
+ { id: "app.component.table.edit", defaultMessage: "Edit {target}" },
925
+ { target: `${role.name}` }
926
+ ),
927
+ children: /* @__PURE__ */ jsx(Pencil, {})
928
+ }
929
+ ) : null,
930
+ checkCanDeleteRole(role) && /* @__PURE__ */ jsx(
931
+ IconButton,
932
+ {
933
+ onClick: () => handleClickDelete(role.id.toString()),
934
+ noBorder: true,
935
+ icon: /* @__PURE__ */ jsx(Trash, {}),
936
+ label: formatMessage(
937
+ { id: "global.delete-target", defaultMessage: "Delete {target}" },
938
+ { target: `${role.name}` }
939
+ )
940
+ }
941
+ )
942
+ ] }) })
943
+ ] }, role.name)) });
944
+ };
945
+ TableBody.defaultProps = {
946
+ canDelete: false,
947
+ canUpdate: false
948
+ };
949
+ TableBody.propTypes = {
950
+ onDelete: PropTypes.array.isRequired,
951
+ permissions: PropTypes.object.isRequired,
952
+ setRoleToDelete: PropTypes.func.isRequired,
953
+ sortedRoles: PropTypes.array.isRequired,
954
+ canDelete: PropTypes.bool,
955
+ canUpdate: PropTypes.bool
956
+ };
957
+ const RolesListPage = () => {
958
+ const { trackUsage } = useTracking();
959
+ const { formatMessage, locale } = useIntl();
960
+ const { toggleNotification } = useNotification();
961
+ const { notifyStatus } = useNotifyAT();
962
+ const [{ query }] = useQueryParams();
963
+ const _q = query?._q || "";
964
+ const [showConfirmDelete, setShowConfirmDelete] = useState(false);
965
+ const [roleToDelete, setRoleToDelete] = useState();
966
+ const { del, get: get2 } = useFetchClient();
967
+ const {
968
+ isLoading: isLoadingForPermissions,
969
+ allowedActions: { canRead, canDelete, canCreate, canUpdate }
970
+ } = useRBAC({
971
+ create: PERMISSIONS.createRole,
972
+ read: PERMISSIONS.readRoles,
973
+ update: PERMISSIONS.updateRole,
974
+ delete: PERMISSIONS.deleteRole
975
+ });
976
+ const {
977
+ isLoading: isLoadingForData,
978
+ data: { roles },
979
+ isFetching,
980
+ refetch
981
+ } = useQuery("get-roles", () => fetchData(toggleNotification, formatMessage, notifyStatus), {
982
+ initialData: {},
983
+ enabled: canRead
984
+ });
985
+ const { contains } = useFilter(locale, {
986
+ sensitivity: "base"
987
+ });
988
+ const formatter = useCollator(locale, {
989
+ sensitivity: "base"
990
+ });
991
+ const isLoading = isLoadingForData || isFetching || isLoadingForPermissions;
992
+ const handleShowConfirmDelete = () => {
993
+ setShowConfirmDelete(!showConfirmDelete);
994
+ };
995
+ const deleteData = async (id, formatMessage2, toggleNotification2) => {
996
+ try {
997
+ await del(`/users-permissions/roles/${id}`);
998
+ } catch (error) {
999
+ toggleNotification2({
1000
+ type: "danger",
1001
+ message: formatMessage2({ id: "notification.error", defaultMessage: "An error occured" })
1002
+ });
1003
+ }
1004
+ };
1005
+ const fetchData = async (toggleNotification2, formatMessage2, notifyStatus2) => {
1006
+ try {
1007
+ const { data } = await get2("/users-permissions/roles");
1008
+ notifyStatus2("The roles have loaded successfully");
1009
+ return data;
1010
+ } catch (err) {
1011
+ toggleNotification2({
1012
+ type: "danger",
1013
+ message: formatMessage2({ id: "notification.error", defaultMessage: "An error occurred" })
1014
+ });
1015
+ throw new Error(err);
1016
+ }
1017
+ };
1018
+ const emptyLayout = {
1019
+ roles: {
1020
+ id: getTrad("Roles.empty"),
1021
+ defaultMessage: "You don't have any roles yet."
1022
+ },
1023
+ search: {
1024
+ id: getTrad("Roles.empty.search"),
1025
+ defaultMessage: "No roles match the search."
1026
+ }
1027
+ };
1028
+ const pageTitle = formatMessage({
1029
+ id: "global.roles",
1030
+ defaultMessage: "Roles"
1031
+ });
1032
+ const deleteMutation = useMutation((id) => deleteData(id, formatMessage, toggleNotification), {
1033
+ async onSuccess() {
1034
+ await refetch();
1035
+ }
1036
+ });
1037
+ const handleConfirmDelete = async () => {
1038
+ await deleteMutation.mutateAsync(roleToDelete);
1039
+ setShowConfirmDelete(!showConfirmDelete);
1040
+ };
1041
+ const sortedRoles = (roles || []).filter((role) => contains(role.name, _q) || contains(role.description, _q)).sort(
1042
+ (a, b) => formatter.compare(a.name, b.name) || formatter.compare(a.description, b.description)
1043
+ );
1044
+ const emptyContent = _q && !sortedRoles.length ? "search" : "roles";
1045
+ const colCount = 4;
1046
+ const rowCount = (roles?.length || 0) + 1;
1047
+ if (isLoading) {
1048
+ return /* @__PURE__ */ jsx(Page.Loading, {});
1049
+ }
1050
+ return /* @__PURE__ */ jsxs(Layouts.Root, { children: [
1051
+ /* @__PURE__ */ jsx(Page.Title, { children: formatMessage(
1052
+ { id: "Settings.PageTitle", defaultMessage: "Settings - {name}" },
1053
+ { name: pageTitle }
1054
+ ) }),
1055
+ /* @__PURE__ */ jsxs(Page.Main, { children: [
1056
+ /* @__PURE__ */ jsx(
1057
+ Layouts.Header,
1058
+ {
1059
+ title: formatMessage({
1060
+ id: "global.roles",
1061
+ defaultMessage: "Roles"
1062
+ }),
1063
+ subtitle: formatMessage({
1064
+ id: "Settings.roles.list.description",
1065
+ defaultMessage: "List of roles"
1066
+ }),
1067
+ primaryAction: canCreate ? /* @__PURE__ */ jsx(
1068
+ LinkButton,
1069
+ {
1070
+ to: "new",
1071
+ tag: NavLink,
1072
+ onClick: () => trackUsage("willCreateRole"),
1073
+ startIcon: /* @__PURE__ */ jsx(Plus, {}),
1074
+ size: "S",
1075
+ children: formatMessage({
1076
+ id: getTrad("List.button.roles"),
1077
+ defaultMessage: "Add new role"
1078
+ })
1079
+ }
1080
+ ) : null,
1081
+ navigationAction: /* @__PURE__ */ jsx(BackButton, {})
1082
+ }
1083
+ ),
1084
+ /* @__PURE__ */ jsx(
1085
+ Layouts.Action,
1086
+ {
1087
+ startActions: /* @__PURE__ */ jsx(
1088
+ SearchInput,
1089
+ {
1090
+ label: formatMessage({
1091
+ id: "app.component.search.label",
1092
+ defaultMessage: "Search"
1093
+ })
1094
+ }
1095
+ )
1096
+ }
1097
+ ),
1098
+ /* @__PURE__ */ jsxs(Layouts.Content, { children: [
1099
+ !canRead && /* @__PURE__ */ jsx(Page.NoPermissions, {}),
1100
+ canRead && sortedRoles && sortedRoles?.length ? /* @__PURE__ */ jsxs(Table, { colCount, rowCount, children: [
1101
+ /* @__PURE__ */ jsx(Thead, { children: /* @__PURE__ */ jsxs(Tr, { children: [
1102
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: formatMessage({ id: "global.name", defaultMessage: "Name" }) }) }),
1103
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: formatMessage({
1104
+ id: "global.description",
1105
+ defaultMessage: "Description"
1106
+ }) }) }),
1107
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(Typography, { variant: "sigma", textColor: "neutral600", children: formatMessage({
1108
+ id: "global.users",
1109
+ defaultMessage: "Users"
1110
+ }) }) }),
1111
+ /* @__PURE__ */ jsx(Th, { children: /* @__PURE__ */ jsx(VisuallyHidden, { children: formatMessage({
1112
+ id: "global.actions",
1113
+ defaultMessage: "Actions"
1114
+ }) }) })
1115
+ ] }) }),
1116
+ /* @__PURE__ */ jsx(
1117
+ TableBody,
1118
+ {
1119
+ sortedRoles,
1120
+ canDelete,
1121
+ canUpdate,
1122
+ permissions: PERMISSIONS,
1123
+ setRoleToDelete,
1124
+ onDelete: [showConfirmDelete, setShowConfirmDelete]
1125
+ }
1126
+ )
1127
+ ] }) : /* @__PURE__ */ jsx(EmptyStateLayout, { content: formatMessage(emptyLayout[emptyContent]) })
1128
+ ] }),
1129
+ /* @__PURE__ */ jsx(
1130
+ ConfirmDialog,
1131
+ {
1132
+ onConfirm: handleConfirmDelete,
1133
+ onClose: handleShowConfirmDelete,
1134
+ isOpen: showConfirmDelete
1135
+ }
1136
+ )
1137
+ ] })
1138
+ ] });
1139
+ };
1140
+ const ProtectedRolesListPage = () => {
1141
+ return /* @__PURE__ */ jsx(Page.Protect, { permissions: PERMISSIONS.accessRoles, children: /* @__PURE__ */ jsx(RolesListPage, {}) });
1142
+ };
1143
+ const Roles = () => {
1144
+ return /* @__PURE__ */ jsx(Page.Protect, { permissions: PERMISSIONS.accessRoles, children: /* @__PURE__ */ jsxs(Routes, { children: [
1145
+ /* @__PURE__ */ jsx(Route, { index: true, element: /* @__PURE__ */ jsx(ProtectedRolesListPage, {}) }),
1146
+ /* @__PURE__ */ jsx(Route, { path: "new", element: /* @__PURE__ */ jsx(ProtectedRolesCreatePage, {}) }),
1147
+ /* @__PURE__ */ jsx(Route, { path: ":id", element: /* @__PURE__ */ jsx(ProtectedRolesEditPage, {}) })
1148
+ ] }) });
1149
+ };
1150
+ export {
1151
+ Roles as default
1152
+ };
1153
+ //# sourceMappingURL=index-B-kWBHFg.mjs.map