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

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