@openedx/frontend-app-admin-console 1.0.0-alpha.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (236) hide show
  1. package/LICENSE +661 -0
  2. package/README.rst +164 -0
  3. package/dist/Main.d.ts +3 -0
  4. package/dist/Main.js +15 -0
  5. package/dist/Main.js.map +1 -0
  6. package/dist/app.d.ts +3 -0
  7. package/dist/app.js +10 -0
  8. package/dist/app.js.map +1 -0
  9. package/dist/authz-module/audit-user/index.d.ts +2 -0
  10. package/dist/authz-module/audit-user/index.js +191 -0
  11. package/dist/authz-module/audit-user/index.js.map +1 -0
  12. package/dist/authz-module/audit-user/messages.d.ts +58 -0
  13. package/dist/authz-module/audit-user/messages.js +60 -0
  14. package/dist/authz-module/audit-user/messages.js.map +1 -0
  15. package/dist/authz-module/authz-home/index.d.ts +2 -0
  16. package/dist/authz-module/authz-home/index.js +23 -0
  17. package/dist/authz-module/authz-home/index.js.map +1 -0
  18. package/dist/authz-module/authz-home/messages.d.ts +23 -0
  19. package/dist/authz-module/authz-home/messages.js +25 -0
  20. package/dist/authz-module/authz-home/messages.js.map +1 -0
  21. package/dist/authz-module/components/AddRoleButton.d.ts +6 -0
  22. package/dist/authz-module/components/AddRoleButton.js +22 -0
  23. package/dist/authz-module/components/AddRoleButton.js.map +1 -0
  24. package/dist/authz-module/components/AnchorButton.d.ts +2 -0
  25. package/dist/authz-module/components/AnchorButton.js +35 -0
  26. package/dist/authz-module/components/AnchorButton.js.map +1 -0
  27. package/dist/authz-module/components/AuthZLayout.d.ts +12 -0
  28. package/dist/authz-module/components/AuthZLayout.js +19 -0
  29. package/dist/authz-module/components/AuthZLayout.js.map +1 -0
  30. package/dist/authz-module/components/AuthZTitle.d.ts +20 -0
  31. package/dist/authz-module/components/AuthZTitle.js +22 -0
  32. package/dist/authz-module/components/AuthZTitle.js.map +1 -0
  33. package/dist/authz-module/components/ConfirmDeletionModal.d.ts +15 -0
  34. package/dist/authz-module/components/ConfirmDeletionModal.js +21 -0
  35. package/dist/authz-module/components/ConfirmDeletionModal.js.map +1 -0
  36. package/dist/authz-module/components/ErrorPage/index.d.ts +3 -0
  37. package/dist/authz-module/components/ErrorPage/index.js +59 -0
  38. package/dist/authz-module/components/ErrorPage/index.js.map +1 -0
  39. package/dist/authz-module/components/ErrorPage/messages.d.ts +53 -0
  40. package/dist/authz-module/components/ErrorPage/messages.js +55 -0
  41. package/dist/authz-module/components/ErrorPage/messages.js.map +1 -0
  42. package/dist/authz-module/components/PermissionTable.d.ts +9 -0
  43. package/dist/authz-module/components/PermissionTable.js +24 -0
  44. package/dist/authz-module/components/PermissionTable.js.map +1 -0
  45. package/dist/authz-module/components/RenderAdminRole.d.ts +5 -0
  46. package/dist/authz-module/components/RenderAdminRole.js +13 -0
  47. package/dist/authz-module/components/RenderAdminRole.js.map +1 -0
  48. package/dist/authz-module/components/RenderPermissionColumn.d.ts +6 -0
  49. package/dist/authz-module/components/RenderPermissionColumn.js +8 -0
  50. package/dist/authz-module/components/RenderPermissionColumn.js.map +1 -0
  51. package/dist/authz-module/components/RenderPermissionInLine.d.ts +6 -0
  52. package/dist/authz-module/components/RenderPermissionInLine.js +8 -0
  53. package/dist/authz-module/components/RenderPermissionInLine.js.map +1 -0
  54. package/dist/authz-module/components/ResourceTooltip.d.ts +11 -0
  55. package/dist/authz-module/components/ResourceTooltip.js +9 -0
  56. package/dist/authz-module/components/ResourceTooltip.js.map +1 -0
  57. package/dist/authz-module/components/RoleCard/constants.d.ts +14 -0
  58. package/dist/authz-module/components/RoleCard/constants.js +15 -0
  59. package/dist/authz-module/components/RoleCard/constants.js.map +1 -0
  60. package/dist/authz-module/components/RoleCard/messages.d.ts +73 -0
  61. package/dist/authz-module/components/RoleCard/messages.js +75 -0
  62. package/dist/authz-module/components/RoleCard/messages.js.map +1 -0
  63. package/dist/authz-module/components/TableCells.d.ts +29 -0
  64. package/dist/authz-module/components/TableCells.js +137 -0
  65. package/dist/authz-module/components/TableCells.js.map +1 -0
  66. package/dist/authz-module/components/TableControlBar/MultipleChoiceFilter.d.ts +3 -0
  67. package/dist/authz-module/components/TableControlBar/MultipleChoiceFilter.js +47 -0
  68. package/dist/authz-module/components/TableControlBar/MultipleChoiceFilter.js.map +1 -0
  69. package/dist/authz-module/components/TableControlBar/OrgFilter.d.ts +5 -0
  70. package/dist/authz-module/components/TableControlBar/OrgFilter.js +36 -0
  71. package/dist/authz-module/components/TableControlBar/OrgFilter.js.map +1 -0
  72. package/dist/authz-module/components/TableControlBar/RolesFilter.d.ts +4 -0
  73. package/dist/authz-module/components/TableControlBar/RolesFilter.js +29 -0
  74. package/dist/authz-module/components/TableControlBar/RolesFilter.js.map +1 -0
  75. package/dist/authz-module/components/TableControlBar/ScopesFilter.d.ts +4 -0
  76. package/dist/authz-module/components/TableControlBar/ScopesFilter.js +45 -0
  77. package/dist/authz-module/components/TableControlBar/ScopesFilter.js.map +1 -0
  78. package/dist/authz-module/components/TableControlBar/SearchFilter.d.ts +7 -0
  79. package/dist/authz-module/components/TableControlBar/SearchFilter.js +8 -0
  80. package/dist/authz-module/components/TableControlBar/SearchFilter.js.map +1 -0
  81. package/dist/authz-module/components/TableControlBar/TableControlBar.d.ts +5 -0
  82. package/dist/authz-module/components/TableControlBar/TableControlBar.js +99 -0
  83. package/dist/authz-module/components/TableControlBar/TableControlBar.js.map +1 -0
  84. package/dist/authz-module/components/TableControlBar/types.d.ts +18 -0
  85. package/dist/authz-module/components/TableControlBar/types.js +2 -0
  86. package/dist/authz-module/components/TableControlBar/types.js.map +1 -0
  87. package/dist/authz-module/components/TableFooter/TableFooter.d.ts +2 -0
  88. package/dist/authz-module/components/TableFooter/TableFooter.js +15 -0
  89. package/dist/authz-module/components/TableFooter/TableFooter.js.map +1 -0
  90. package/dist/authz-module/components/UserPermissions.d.ts +9 -0
  91. package/dist/authz-module/components/UserPermissions.js +56 -0
  92. package/dist/authz-module/components/UserPermissions.js.map +1 -0
  93. package/dist/authz-module/components/ViewMoreLink.d.ts +8 -0
  94. package/dist/authz-module/components/ViewMoreLink.js +8 -0
  95. package/dist/authz-module/components/ViewMoreLink.js.map +1 -0
  96. package/dist/authz-module/components/constants.d.ts +13 -0
  97. package/dist/authz-module/components/constants.js +80 -0
  98. package/dist/authz-module/components/constants.js.map +1 -0
  99. package/dist/authz-module/components/messages.d.ts +183 -0
  100. package/dist/authz-module/components/messages.js +185 -0
  101. package/dist/authz-module/components/messages.js.map +1 -0
  102. package/dist/authz-module/constants.d.ts +38 -0
  103. package/dist/authz-module/constants.js +77 -0
  104. package/dist/authz-module/constants.js.map +1 -0
  105. package/dist/authz-module/data/api.d.ts +109 -0
  106. package/dist/authz-module/data/api.js +107 -0
  107. package/dist/authz-module/data/api.js.map +1 -0
  108. package/dist/authz-module/data/hooks.d.ts +94 -0
  109. package/dist/authz-module/data/hooks.js +191 -0
  110. package/dist/authz-module/data/hooks.js.map +1 -0
  111. package/dist/authz-module/hooks/useCourseAuthoringFlag.d.ts +26 -0
  112. package/dist/authz-module/hooks/useCourseAuthoringFlag.js +86 -0
  113. package/dist/authz-module/hooks/useCourseAuthoringFlag.js.map +1 -0
  114. package/dist/authz-module/hooks/useQuerySettings.d.ts +27 -0
  115. package/dist/authz-module/hooks/useQuerySettings.js +72 -0
  116. package/dist/authz-module/hooks/useQuerySettings.js.map +1 -0
  117. package/dist/authz-module/hooks/useViewTeamPermissions.d.ts +5 -0
  118. package/dist/authz-module/hooks/useViewTeamPermissions.js +13 -0
  119. package/dist/authz-module/hooks/useViewTeamPermissions.js.map +1 -0
  120. package/dist/authz-module/index.d.ts +3 -0
  121. package/dist/authz-module/index.js +20 -0
  122. package/dist/authz-module/index.js.map +1 -0
  123. package/dist/authz-module/index.scss +131 -0
  124. package/dist/authz-module/messages.d.ts +48 -0
  125. package/dist/authz-module/messages.js +50 -0
  126. package/dist/authz-module/messages.js.map +1 -0
  127. package/dist/authz-module/role-assignation-wizard/AssignRoleWizard.d.ts +8 -0
  128. package/dist/authz-module/role-assignation-wizard/AssignRoleWizard.js +154 -0
  129. package/dist/authz-module/role-assignation-wizard/AssignRoleWizard.js.map +1 -0
  130. package/dist/authz-module/role-assignation-wizard/AssignRoleWizardPage.d.ts +2 -0
  131. package/dist/authz-module/role-assignation-wizard/AssignRoleWizardPage.js +39 -0
  132. package/dist/authz-module/role-assignation-wizard/AssignRoleWizardPage.js.map +1 -0
  133. package/dist/authz-module/role-assignation-wizard/components/DefineApplicationScopeStep.d.ts +8 -0
  134. package/dist/authz-module/role-assignation-wizard/components/DefineApplicationScopeStep.js +39 -0
  135. package/dist/authz-module/role-assignation-wizard/components/DefineApplicationScopeStep.js.map +1 -0
  136. package/dist/authz-module/role-assignation-wizard/components/HighlightedUsersInput.d.ts +11 -0
  137. package/dist/authz-module/role-assignation-wizard/components/HighlightedUsersInput.js +36 -0
  138. package/dist/authz-module/role-assignation-wizard/components/HighlightedUsersInput.js.map +1 -0
  139. package/dist/authz-module/role-assignation-wizard/components/OrgSection.d.ts +10 -0
  140. package/dist/authz-module/role-assignation-wizard/components/OrgSection.js +14 -0
  141. package/dist/authz-module/role-assignation-wizard/components/OrgSection.js.map +1 -0
  142. package/dist/authz-module/role-assignation-wizard/components/ScopeCheckboxItem.d.ts +8 -0
  143. package/dist/authz-module/role-assignation-wizard/components/ScopeCheckboxItem.js +5 -0
  144. package/dist/authz-module/role-assignation-wizard/components/ScopeCheckboxItem.js.map +1 -0
  145. package/dist/authz-module/role-assignation-wizard/components/ScopeFilterBar.d.ts +12 -0
  146. package/dist/authz-module/role-assignation-wizard/components/ScopeFilterBar.js +12 -0
  147. package/dist/authz-module/role-assignation-wizard/components/ScopeFilterBar.js.map +1 -0
  148. package/dist/authz-module/role-assignation-wizard/components/ScopeList.d.ts +20 -0
  149. package/dist/authz-module/role-assignation-wizard/components/ScopeList.js +30 -0
  150. package/dist/authz-module/role-assignation-wizard/components/ScopeList.js.map +1 -0
  151. package/dist/authz-module/role-assignation-wizard/components/SelectUsersAndRoleStep.d.ts +12 -0
  152. package/dist/authz-module/role-assignation-wizard/components/SelectUsersAndRoleStep.js +34 -0
  153. package/dist/authz-module/role-assignation-wizard/components/SelectUsersAndRoleStep.js.map +1 -0
  154. package/dist/authz-module/role-assignation-wizard/constants.d.ts +5 -0
  155. package/dist/authz-module/role-assignation-wizard/constants.js +6 -0
  156. package/dist/authz-module/role-assignation-wizard/constants.js.map +1 -0
  157. package/dist/authz-module/role-assignation-wizard/hooks/useScopeListData.d.ts +23 -0
  158. package/dist/authz-module/role-assignation-wizard/hooks/useScopeListData.js +112 -0
  159. package/dist/authz-module/role-assignation-wizard/hooks/useScopeListData.js.map +1 -0
  160. package/dist/authz-module/role-assignation-wizard/hooks/useScopePermissions.d.ts +10 -0
  161. package/dist/authz-module/role-assignation-wizard/hooks/useScopePermissions.js +54 -0
  162. package/dist/authz-module/role-assignation-wizard/hooks/useScopePermissions.js.map +1 -0
  163. package/dist/authz-module/role-assignation-wizard/messages.d.ts +243 -0
  164. package/dist/authz-module/role-assignation-wizard/messages.js +258 -0
  165. package/dist/authz-module/role-assignation-wizard/messages.js.map +1 -0
  166. package/dist/authz-module/role-assignation-wizard/utils.d.ts +5 -0
  167. package/dist/authz-module/role-assignation-wizard/utils.js +16 -0
  168. package/dist/authz-module/role-assignation-wizard/utils.js.map +1 -0
  169. package/dist/authz-module/roles-permissions/RolesPermissions.d.ts +2 -0
  170. package/dist/authz-module/roles-permissions/RolesPermissions.js +35 -0
  171. package/dist/authz-module/roles-permissions/RolesPermissions.js.map +1 -0
  172. package/dist/authz-module/roles-permissions/course/constants.d.ts +39 -0
  173. package/dist/authz-module/roles-permissions/course/constants.js +474 -0
  174. package/dist/authz-module/roles-permissions/course/constants.js.map +1 -0
  175. package/dist/authz-module/roles-permissions/index.d.ts +10 -0
  176. package/dist/authz-module/roles-permissions/index.js +15 -0
  177. package/dist/authz-module/roles-permissions/index.js.map +1 -0
  178. package/dist/authz-module/roles-permissions/library/constants.d.ts +18 -0
  179. package/dist/authz-module/roles-permissions/library/constants.js +193 -0
  180. package/dist/authz-module/roles-permissions/library/constants.js.map +1 -0
  181. package/dist/authz-module/roles-permissions/messages.d.ts +63 -0
  182. package/dist/authz-module/roles-permissions/messages.js +65 -0
  183. package/dist/authz-module/roles-permissions/messages.js.map +1 -0
  184. package/dist/authz-module/roles-permissions/utils.d.ts +41 -0
  185. package/dist/authz-module/roles-permissions/utils.js +70 -0
  186. package/dist/authz-module/roles-permissions/utils.js.map +1 -0
  187. package/dist/authz-module/team-members/TeamMembersTable.d.ts +5 -0
  188. package/dist/authz-module/team-members/TeamMembersTable.js +107 -0
  189. package/dist/authz-module/team-members/TeamMembersTable.js.map +1 -0
  190. package/dist/authz-module/team-members/messages.d.ts +33 -0
  191. package/dist/authz-module/team-members/messages.js +35 -0
  192. package/dist/authz-module/team-members/messages.js.map +1 -0
  193. package/dist/authz-module/utils.d.ts +24 -0
  194. package/dist/authz-module/utils.js +45 -0
  195. package/dist/authz-module/utils.js.map +1 -0
  196. package/dist/components/LoadingPage.d.ts +2 -0
  197. package/dist/components/LoadingPage.js +5 -0
  198. package/dist/components/LoadingPage.js.map +1 -0
  199. package/dist/components/ToastManager/ToastManagerContext.d.ts +24 -0
  200. package/dist/components/ToastManager/ToastManagerContext.js +91 -0
  201. package/dist/components/ToastManager/ToastManagerContext.js.map +1 -0
  202. package/dist/constants.d.ts +11 -0
  203. package/dist/constants.js +15 -0
  204. package/dist/constants.js.map +1 -0
  205. package/dist/data/api.d.ts +4 -0
  206. package/dist/data/api.js +21 -0
  207. package/dist/data/api.js.map +1 -0
  208. package/dist/data/hooks.d.ts +38 -0
  209. package/dist/data/hooks.js +69 -0
  210. package/dist/data/hooks.js.map +1 -0
  211. package/dist/data/types.d.ts +38 -0
  212. package/dist/data/types.js +2 -0
  213. package/dist/data/types.js.map +1 -0
  214. package/dist/data/utils.d.ts +6 -0
  215. package/dist/data/utils.js +11 -0
  216. package/dist/data/utils.js.map +1 -0
  217. package/dist/i18n/index.d.ts +1 -0
  218. package/dist/i18n/index.js +2 -0
  219. package/dist/i18n/index.js.map +1 -0
  220. package/dist/index.d.ts +3 -0
  221. package/dist/index.js +4 -0
  222. package/dist/index.js.map +1 -0
  223. package/dist/messages.d.ts +8 -0
  224. package/dist/messages.js +10 -0
  225. package/dist/messages.js.map +1 -0
  226. package/dist/providers.d.ts +3 -0
  227. package/dist/providers.js +4 -0
  228. package/dist/providers.js.map +1 -0
  229. package/dist/routes.d.ts +11 -0
  230. package/dist/routes.js +16 -0
  231. package/dist/routes.js.map +1 -0
  232. package/dist/style.scss +15 -0
  233. package/dist/types.d.ts +93 -0
  234. package/dist/types.js +2 -0
  235. package/dist/types.js.map +1 -0
  236. package/package.json +88 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"AssignRoleWizardPage.js","sourceRoot":"","sources":["../../../src/authz-module/role-assignation-wizard/AssignRoleWizardPage.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useNavigate, useSearchParams } from 'react-router-dom';\nimport { useIntl } from '@openedx/frontend-base';\nimport { useValidateUserPermissionsNonSuspense } from '@src/data/hooks';\nimport AssignRoleWizard from './AssignRoleWizard';\nimport AuthZLayout from '@src/authz-module/components/AuthZLayout';\nimport { ROUTES } from '@src/authz-module/constants';\nimport messages from './messages';\nimport {\n CONTENT_COURSE_PERMISSIONS, CONTENT_LIBRARY_PERMISSIONS, courseRolesMetadata, libraryRolesMetadata,\n MANAGE_TEAM_PERMISSIONS,\n} from '@src/authz-module/roles-permissions';\nimport { useCourseAuthoringFlag } from '@src/authz-module/hooks/useCourseAuthoringFlag';\n\nconst AssignRoleWizardPage = () => {\n const intl = useIntl();\n const navigate = useNavigate();\n const [searchParams] = useSearchParams();\n const initialUsers = searchParams.get('users') || '';\n const raw = searchParams.get('from') ?? '';\n const returnTo = (raw.startsWith('/') && !raw.startsWith('//')) ? raw : ROUTES.HOME_PATH;\n\n const presetUser = initialUsers.trim();\n const destination = (presetUser && !presetUser.includes(','))\n ? `${ROUTES.HOME_PATH}/user/${presetUser}`\n : returnTo;\n\n const { data: managePermissions } = useValidateUserPermissionsNonSuspense(MANAGE_TEAM_PERMISSIONS);\n const { isCourseAuthoringEnabled } = useCourseAuthoringFlag();\n\n const rolesAssignable = managePermissions?.flatMap((p) => {\n if (!p.allowed) return [];\n if (p.action === CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TEAM) return libraryRolesMetadata;\n if (p.action === CONTENT_COURSE_PERMISSIONS.MANAGE_COURSE_TEAM) {\n // Course (authoring) roles are only assignable when the course-authoring flag is enabled.\n return isCourseAuthoringEnabled ? courseRolesMetadata : [];\n }\n return [];\n });\n\n return (\n <AuthZLayout\n context={{ id: '', title: '', org: '' }}\n navLinks={[{ label: intl.formatMessage(messages['wizard.page.breadcrumb']), to: returnTo }]}\n activeLabel={intl.formatMessage(messages['wizard.page.title'])}\n pageTitle={intl.formatMessage(messages['wizard.page.title'])}\n pageSubtitle=\"\"\n actions={[]}\n >\n <AssignRoleWizard\n onClose={() => navigate(destination)}\n initialUsers={initialUsers}\n roles={rolesAssignable}\n />\n </AuthZLayout>\n );\n};\n\nexport default AssignRoleWizardPage;\n"]}
@@ -0,0 +1,8 @@
1
+ interface DefineApplicationScopeStepProps {
2
+ selectedRole: string | null;
3
+ selectedScopes: Set<string>;
4
+ onScopeToggle: (scopeId: string) => void;
5
+ assignmentErrors?: string[];
6
+ }
7
+ declare const DefineApplicationScopeStep: ({ selectedRole, selectedScopes, onScopeToggle, assignmentErrors, }: DefineApplicationScopeStepProps) => import("react").JSX.Element;
8
+ export default DefineApplicationScopeStep;
@@ -0,0 +1,39 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState, useEffect, useMemo } from 'react';
3
+ import { useIntl } from '@openedx/frontend-base';
4
+ import { Alert } from '@openedx/paragon';
5
+ import { courseRolesMetadata } from '../../roles-permissions/course/constants';
6
+ import { libraryRolesMetadata } from '../../roles-permissions/library/constants';
7
+ import useScopeListData from '../hooks/useScopeListData';
8
+ import ScopeFilterBar from './ScopeFilterBar';
9
+ import ScopeList from './ScopeList';
10
+ import messages from '../messages';
11
+ const allRolesMetadata = [...courseRolesMetadata, ...libraryRolesMetadata];
12
+ function getContextType(role) {
13
+ var _a;
14
+ if (!role)
15
+ return undefined;
16
+ return (_a = allRolesMetadata.find((r) => r.role === role)) === null || _a === void 0 ? void 0 : _a.contextType;
17
+ }
18
+ const DefineApplicationScopeStep = ({ selectedRole, selectedScopes, onScopeToggle, assignmentErrors = [], }) => {
19
+ const intl = useIntl();
20
+ const [search, setSearch] = useState('');
21
+ const [debouncedSearch, setDebouncedSearch] = useState('');
22
+ const [selectedOrgs, setSelectedOrgs] = useState([]);
23
+ useEffect(() => {
24
+ const timer = setTimeout(() => setDebouncedSearch(search), 300);
25
+ return () => clearTimeout(timer);
26
+ }, [search]);
27
+ const contextType = useMemo(() => getContextType(selectedRole), [selectedRole]);
28
+ const contextLabelMessages = {
29
+ course: messages['wizard.step2.contextLabel.course'],
30
+ library: messages['wizard.step2.contextLabel.library'],
31
+ };
32
+ const contextLabel = contextType && contextLabelMessages[contextType]
33
+ ? intl.formatMessage(contextLabelMessages[contextType])
34
+ : intl.formatMessage(messages['wizard.step2.contextLabel.default']);
35
+ const { organizations, orderedOrgs, scopesByOrg, allScopes, totalCount, queryState, platformAggregateScopeItem, orgAggregateScopeItems, } = useScopeListData({ contextType, search: debouncedSearch, orgs: selectedOrgs });
36
+ return (_jsxs("div", { className: "define-application-scope-step", children: [_jsx("h3", { className: "mb-4", children: intl.formatMessage(messages['wizard.step.defineScope.title']) }), _jsx(ScopeFilterBar, { search: search, onSearchChange: setSearch, selectedOrgs: selectedOrgs, onOrgsChange: setSelectedOrgs, contextType: contextType, contextLabel: contextLabel, allScopesCount: allScopes.length, totalCount: totalCount }), _jsx("hr", { className: "my-4" }), assignmentErrors.length > 0 && (_jsxs(Alert, { variant: "danger", className: "mb-4", children: [_jsx("p", { className: "mb-1 font-weight-bold", children: intl.formatMessage(messages['wizard.step2.errors.alert.title']) }), _jsx("ul", { className: "mb-0 pl-3", children: assignmentErrors.map((msg) => _jsx("li", { children: msg }, msg)) })] })), _jsx(ScopeList, { orderedOrgs: orderedOrgs, scopesByOrg: scopesByOrg, organizations: organizations, selectedScopes: selectedScopes, onScopeToggle: onScopeToggle, queryState: queryState, platformAggregateScopeItem: platformAggregateScopeItem, orgAggregateScopeItems: orgAggregateScopeItems })] }));
37
+ };
38
+ export default DefineApplicationScopeStep;
39
+ //# sourceMappingURL=DefineApplicationScopeStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"DefineApplicationScopeStep.js","sourceRoot":"","sources":["../../../../src/authz-module/role-assignation-wizard/components/DefineApplicationScopeStep.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useState, useEffect, useMemo } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Alert } from '@openedx/paragon';\nimport { courseRolesMetadata } from '@src/authz-module/roles-permissions/course/constants';\nimport { libraryRolesMetadata } from '@src/authz-module/roles-permissions/library/constants';\nimport useScopeListData from '@src/authz-module/role-assignation-wizard/hooks/useScopeListData';\nimport ScopeFilterBar from './ScopeFilterBar';\nimport ScopeList from './ScopeList';\nimport messages from '@src/authz-module/role-assignation-wizard/messages';\n\nconst allRolesMetadata = [...courseRolesMetadata, ...libraryRolesMetadata];\n\nfunction getContextType(role: string | null): string | undefined {\n if (!role) return undefined;\n return allRolesMetadata.find((r) => r.role === role)?.contextType;\n}\n\ninterface DefineApplicationScopeStepProps {\n selectedRole: string | null;\n selectedScopes: Set<string>;\n onScopeToggle: (scopeId: string) => void;\n assignmentErrors?: string[];\n}\n\nconst DefineApplicationScopeStep = ({\n selectedRole,\n selectedScopes,\n onScopeToggle,\n assignmentErrors = [],\n}: DefineApplicationScopeStepProps) => {\n const intl = useIntl();\n const [search, setSearch] = useState('');\n const [debouncedSearch, setDebouncedSearch] = useState('');\n const [selectedOrgs, setSelectedOrgs] = useState<string[]>([]);\n\n useEffect(() => {\n const timer = setTimeout(() => setDebouncedSearch(search), 300);\n return () => clearTimeout(timer);\n }, [search]);\n\n const contextType = useMemo(\n () => getContextType(selectedRole),\n [selectedRole],\n );\n const contextLabelMessages = {\n course: messages['wizard.step2.contextLabel.course'],\n library: messages['wizard.step2.contextLabel.library'],\n };\n const contextLabel = contextType && contextLabelMessages[contextType]\n ? intl.formatMessage(contextLabelMessages[contextType])\n : intl.formatMessage(messages['wizard.step2.contextLabel.default']);\n\n const {\n organizations,\n orderedOrgs,\n scopesByOrg,\n allScopes,\n totalCount,\n queryState,\n platformAggregateScopeItem,\n orgAggregateScopeItems,\n } = useScopeListData({ contextType, search: debouncedSearch, orgs: selectedOrgs });\n\n return (\n <div className=\"define-application-scope-step\">\n <h3 className=\"mb-4\">{intl.formatMessage(messages['wizard.step.defineScope.title'])}</h3>\n\n <ScopeFilterBar\n search={search}\n onSearchChange={setSearch}\n selectedOrgs={selectedOrgs}\n onOrgsChange={setSelectedOrgs}\n contextType={contextType}\n contextLabel={contextLabel}\n allScopesCount={allScopes.length}\n totalCount={totalCount}\n />\n\n <hr className=\"my-4\" />\n\n {assignmentErrors.length > 0 && (\n <Alert variant=\"danger\" className=\"mb-4\">\n <p className=\"mb-1 font-weight-bold\">\n {intl.formatMessage(messages['wizard.step2.errors.alert.title'])}\n </p>\n <ul className=\"mb-0 pl-3\">\n {assignmentErrors.map((msg) => <li key={msg}>{msg}</li>)}\n </ul>\n </Alert>\n )}\n\n <ScopeList\n orderedOrgs={orderedOrgs}\n scopesByOrg={scopesByOrg}\n organizations={organizations}\n selectedScopes={selectedScopes}\n onScopeToggle={onScopeToggle}\n queryState={queryState}\n platformAggregateScopeItem={platformAggregateScopeItem}\n orgAggregateScopeItems={orgAggregateScopeItems}\n />\n </div>\n );\n};\n\nexport default DefineApplicationScopeStep;\n"]}
@@ -0,0 +1,11 @@
1
+ interface HighlightedUsersInputProps {
2
+ id: string;
3
+ value: string;
4
+ onChange: (val: string) => void;
5
+ invalidUsers: string[];
6
+ placeholder?: string;
7
+ hasError?: boolean;
8
+ ariaDescribedBy?: string;
9
+ }
10
+ declare const HighlightedUsersInput: import("react").ForwardRefExoticComponent<HighlightedUsersInputProps & import("react").RefAttributes<HTMLTextAreaElement>>;
11
+ export default HighlightedUsersInput;
@@ -0,0 +1,36 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo, useRef, useCallback, forwardRef, } from 'react';
3
+ const HighlightedUsersInput = forwardRef(({ id, value, onChange, invalidUsers, placeholder, hasError = false, ariaDescribedBy, }, ref) => {
4
+ const overlayRef = useRef(null);
5
+ const invalidSet = useMemo(() => new Set(invalidUsers.map((u) => u.trim())), [invalidUsers]);
6
+ const hasHighlights = invalidSet.size > 0;
7
+ const renderedParts = useMemo(() => {
8
+ if (!hasHighlights)
9
+ return null;
10
+ let offset = 0;
11
+ return value.split(/(,)/).map((part) => {
12
+ const key = offset;
13
+ offset += part.length;
14
+ if (part === ',')
15
+ return _jsx("span", { children: "," }, `comma-${key}`);
16
+ const trimmed = part.trim();
17
+ const isInvalid = trimmed.length > 0 && invalidSet.has(trimmed);
18
+ return (_jsx("span", { "data-invalid": isInvalid || undefined, className: isInvalid ? 'text-danger' : '', children: part }, `part-${key}`));
19
+ });
20
+ }, [value, invalidSet, hasHighlights]);
21
+ const handleScroll = useCallback((e) => {
22
+ if (overlayRef.current) {
23
+ overlayRef.current.scrollTop = e.currentTarget.scrollTop;
24
+ }
25
+ }, []);
26
+ return (_jsxs("div", { className: "position-relative", children: [hasHighlights && (_jsx("div", { ref: overlayRef, "aria-hidden": "true", className: "highlighted-users-input__overlay highlighted-users-input__layer", children: renderedParts })), _jsx("textarea", { ref: ref, id: id, rows: 4, value: value, onChange: (e) => onChange(e.target.value), onScroll: handleScroll, placeholder: hasHighlights ? undefined : placeholder, "aria-invalid": hasError || undefined, "aria-describedby": ariaDescribedBy, "aria-required": true, className: [
27
+ 'form-control',
28
+ 'highlighted-users-input__textarea',
29
+ 'highlighted-users-input__layer',
30
+ hasHighlights && 'highlighted-users-input__textarea--highlighted',
31
+ hasError && 'is-invalid',
32
+ ].filter(Boolean).join(' ') })] }));
33
+ });
34
+ HighlightedUsersInput.displayName = 'HighlightedUsersInput';
35
+ export default HighlightedUsersInput;
36
+ //# sourceMappingURL=HighlightedUsersInput.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"HighlightedUsersInput.js","sourceRoot":"","sources":["../../../../src/authz-module/role-assignation-wizard/components/HighlightedUsersInput.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n useMemo, useRef, useCallback, forwardRef,\n} from 'react';\n\ninterface HighlightedUsersInputProps {\n id: string;\n value: string;\n onChange: (val: string) => void;\n invalidUsers: string[];\n placeholder?: string;\n hasError?: boolean;\n ariaDescribedBy?: string;\n}\n\nconst HighlightedUsersInput = forwardRef<HTMLTextAreaElement, HighlightedUsersInputProps>(({\n id, value, onChange, invalidUsers, placeholder, hasError = false, ariaDescribedBy,\n}, ref) => {\n const overlayRef = useRef<HTMLDivElement>(null);\n\n const invalidSet = useMemo(\n () => new Set(invalidUsers.map((u) => u.trim())),\n [invalidUsers],\n );\n const hasHighlights = invalidSet.size > 0;\n\n const renderedParts = useMemo(() => {\n if (!hasHighlights) return null;\n let offset = 0;\n return value.split(/(,)/).map((part) => {\n const key = offset;\n offset += part.length;\n if (part === ',') return <span key={`comma-${key}`}>,</span>;\n const trimmed = part.trim();\n const isInvalid = trimmed.length > 0 && invalidSet.has(trimmed);\n return (\n <span\n key={`part-${key}`}\n data-invalid={isInvalid || undefined}\n className={isInvalid ? 'text-danger' : ''}\n >\n {part}\n </span>\n );\n });\n }, [value, invalidSet, hasHighlights]);\n\n const handleScroll = useCallback((e: { currentTarget: HTMLTextAreaElement }) => {\n if (overlayRef.current) {\n overlayRef.current.scrollTop = e.currentTarget.scrollTop;\n }\n }, []);\n\n return (\n <div className=\"position-relative\">\n {/* Highlight layer — sits behind the transparent textarea */}\n {hasHighlights && (\n <div\n ref={overlayRef}\n aria-hidden=\"true\"\n className=\"highlighted-users-input__overlay highlighted-users-input__layer\"\n >\n {renderedParts}\n </div>\n )}\n\n {/* Actual textarea — text is transparent when overlay is active */}\n <textarea\n ref={ref}\n id={id}\n rows={4}\n value={value}\n onChange={(e) => onChange(e.target.value)}\n onScroll={handleScroll}\n placeholder={hasHighlights ? undefined : placeholder}\n aria-invalid={hasError || undefined}\n aria-describedby={ariaDescribedBy}\n aria-required\n className={[\n 'form-control',\n 'highlighted-users-input__textarea',\n 'highlighted-users-input__layer',\n hasHighlights && 'highlighted-users-input__textarea--highlighted',\n hasError && 'is-invalid',\n ].filter(Boolean).join(' ')}\n />\n </div>\n );\n});\n\nHighlightedUsersInput.displayName = 'HighlightedUsersInput';\n\nexport default HighlightedUsersInput;\n"]}
@@ -0,0 +1,10 @@
1
+ import { Scope } from '../../../types';
2
+ export interface OrgSectionProps {
3
+ orgName: string;
4
+ scopes: Scope[];
5
+ selectedScopes: Set<string>;
6
+ onScopeToggle: (scopeId: string) => void;
7
+ aggregateScopeItem?: Scope;
8
+ }
9
+ declare const OrgSection: ({ orgName, scopes, selectedScopes, onScopeToggle, aggregateScopeItem, }: OrgSectionProps) => import("react").JSX.Element;
10
+ export default OrgSection;
@@ -0,0 +1,14 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useState } from 'react';
3
+ import { Icon } from '@openedx/paragon';
4
+ import { ExpandLess, ExpandMore } from '@openedx/paragon/icons';
5
+ import { useIntl } from '@openedx/frontend-base';
6
+ import messages from '../messages';
7
+ import ScopeCheckboxItem from './ScopeCheckboxItem';
8
+ const OrgSection = ({ orgName, scopes, selectedScopes, onScopeToggle, aggregateScopeItem, }) => {
9
+ const intl = useIntl();
10
+ const [collapsed, setCollapsed] = useState(false);
11
+ return (_jsxs("div", { className: "scope-org-group mb-3", children: [_jsxs("button", { type: "button", className: "d-flex align-items-center gap-1 bg-transparent border-0 p-0 mb-2 text-primary font-weight-bold", onClick: () => setCollapsed((prev) => !prev), children: [_jsx("span", { children: intl.formatMessage(messages['wizard.step2.scopeList.orgLabel'], { orgName }) }), _jsx(Icon, { src: collapsed ? ExpandMore : ExpandLess, className: "text-primary" })] }), !collapsed && (_jsxs("div", { className: "pl-2", children: [aggregateScopeItem && (_jsx(ScopeCheckboxItem, { scope: aggregateScopeItem, checked: selectedScopes.has(aggregateScopeItem.externalKey), onToggle: onScopeToggle })), scopes.map((scope) => (_jsx(ScopeCheckboxItem, { scope: scope, checked: selectedScopes.has(scope.externalKey), onToggle: onScopeToggle }, scope.externalKey)))] }))] }));
12
+ };
13
+ export default OrgSection;
14
+ //# sourceMappingURL=OrgSection.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"OrgSection.js","sourceRoot":"","sources":["../../../../src/authz-module/role-assignation-wizard/components/OrgSection.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,QAAQ,EAAE,MAAM,OAAO,CAAC;AACjC,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AAChE,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEjD,OAAO,QAAQ,MAAM,oDAAoD,CAAC;AAC1E,OAAO,iBAAiB,MAAM,qBAAqB,CAAC;AAUpD,MAAM,UAAU,GAAG,CAAC,EAClB,OAAO,EAAE,MAAM,EAAE,cAAc,EAAE,aAAa,EAAE,kBAAkB,GAClD,EAAE,EAAE;IACpB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IACvB,MAAM,CAAC,SAAS,EAAE,YAAY,CAAC,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC;IAElD,OAAO,CACL,eAAK,SAAS,EAAC,sBAAsB,aACnC,kBACE,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,gGAAgG,EAC1G,OAAO,EAAE,GAAG,EAAE,CAAC,YAAY,CAAC,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC,IAAI,CAAC,aAE5C,yBAAO,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,iCAAiC,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,GAAQ,EAC3F,KAAC,IAAI,IAAC,GAAG,EAAE,SAAS,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,UAAU,EAAE,SAAS,EAAC,cAAc,GAAG,IACpE,EAER,CAAC,SAAS,IAAI,CACb,eAAK,SAAS,EAAC,MAAM,aAClB,kBAAkB,IAAI,CACrB,KAAC,iBAAiB,IAChB,KAAK,EAAE,kBAAkB,EACzB,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,kBAAkB,CAAC,WAAW,CAAC,EAC3D,QAAQ,EAAE,aAAa,GACvB,CACH,EACA,MAAM,CAAC,GAAG,CAAC,CAAC,KAAK,EAAE,EAAE,CAAC,CACrB,KAAC,iBAAiB,IAEhB,KAAK,EAAE,KAAK,EACZ,OAAO,EAAE,cAAc,CAAC,GAAG,CAAC,KAAK,CAAC,WAAW,CAAC,EAC9C,QAAQ,EAAE,aAAa,IAHlB,KAAK,CAAC,WAAW,CAItB,CACH,CAAC,IACE,CACP,IACG,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,UAAU,CAAC","sourcesContent":["import { useState } from 'react';\nimport { Icon } from '@openedx/paragon';\nimport { ExpandLess, ExpandMore } from '@openedx/paragon/icons';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Scope } from '@src/types';\nimport messages from '@src/authz-module/role-assignation-wizard/messages';\nimport ScopeCheckboxItem from './ScopeCheckboxItem';\n\nexport interface OrgSectionProps {\n orgName: string;\n scopes: Scope[];\n selectedScopes: Set<string>;\n onScopeToggle: (scopeId: string) => void;\n aggregateScopeItem?: Scope;\n}\n\nconst OrgSection = ({\n orgName, scopes, selectedScopes, onScopeToggle, aggregateScopeItem,\n}: OrgSectionProps) => {\n const intl = useIntl();\n const [collapsed, setCollapsed] = useState(false);\n\n return (\n <div className=\"scope-org-group mb-3\">\n <button\n type=\"button\"\n className=\"d-flex align-items-center gap-1 bg-transparent border-0 p-0 mb-2 text-primary font-weight-bold\"\n onClick={() => setCollapsed((prev) => !prev)}\n >\n <span>{intl.formatMessage(messages['wizard.step2.scopeList.orgLabel'], { orgName })}</span>\n <Icon src={collapsed ? ExpandMore : ExpandLess} className=\"text-primary\" />\n </button>\n\n {!collapsed && (\n <div className=\"pl-2\">\n {aggregateScopeItem && (\n <ScopeCheckboxItem\n scope={aggregateScopeItem}\n checked={selectedScopes.has(aggregateScopeItem.externalKey)}\n onToggle={onScopeToggle}\n />\n )}\n {scopes.map((scope) => (\n <ScopeCheckboxItem\n key={scope.externalKey}\n scope={scope}\n checked={selectedScopes.has(scope.externalKey)}\n onToggle={onScopeToggle}\n />\n ))}\n </div>\n )}\n </div>\n );\n};\n\nexport default OrgSection;\n"]}
@@ -0,0 +1,8 @@
1
+ import { Scope } from '../../../types';
2
+ interface ScopeCheckboxItemProps {
3
+ scope: Scope;
4
+ checked: boolean;
5
+ onToggle: (scopeId: string) => void;
6
+ }
7
+ declare const ScopeCheckboxItem: ({ scope, checked, onToggle }: ScopeCheckboxItemProps) => import("react").JSX.Element;
8
+ export default ScopeCheckboxItem;
@@ -0,0 +1,5 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { Form } from '@openedx/paragon';
3
+ const ScopeCheckboxItem = ({ scope, checked, onToggle }) => (_jsxs("div", { className: "my-4", children: [_jsx(Form.Checkbox, { checked: checked, onChange: () => onToggle(scope.externalKey), "data-testid": `toggle-scope-${scope.externalKey}`, children: scope.displayName }), scope.description && (_jsx("small", { className: "d-block text-muted font-italic pl-4", children: scope.description }))] }));
4
+ export default ScopeCheckboxItem;
5
+ //# sourceMappingURL=ScopeCheckboxItem.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScopeCheckboxItem.js","sourceRoot":"","sources":["../../../../src/authz-module/role-assignation-wizard/components/ScopeCheckboxItem.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AASxC,MAAM,iBAAiB,GAAG,CAAC,EAAE,KAAK,EAAE,OAAO,EAAE,QAAQ,EAA0B,EAAE,EAAE,CAAC,CAClF,eAAK,SAAS,EAAC,MAAM,aACnB,KAAC,IAAI,CAAC,QAAQ,IACZ,OAAO,EAAE,OAAO,EAChB,QAAQ,EAAE,GAAG,EAAE,CAAC,QAAQ,CAAC,KAAK,CAAC,WAAW,CAAC,iBAC9B,gBAAgB,KAAK,CAAC,WAAW,EAAE,YAE/C,KAAK,CAAC,WAAW,GACJ,EACf,KAAK,CAAC,WAAW,IAAI,CACpB,gBAAO,SAAS,EAAC,qCAAqC,YAAE,KAAK,CAAC,WAAW,GAAS,CACnF,IACG,CACP,CAAC;AAEF,eAAe,iBAAiB,CAAC","sourcesContent":["import { Form } from '@openedx/paragon';\nimport { Scope } from '@src/types';\n\ninterface ScopeCheckboxItemProps {\n scope: Scope;\n checked: boolean;\n onToggle: (scopeId: string) => void;\n}\n\nconst ScopeCheckboxItem = ({ scope, checked, onToggle }: ScopeCheckboxItemProps) => (\n <div className=\"my-4\">\n <Form.Checkbox\n checked={checked}\n onChange={() => onToggle(scope.externalKey)}\n data-testid={`toggle-scope-${scope.externalKey}`}\n >\n {scope.displayName}\n </Form.Checkbox>\n {scope.description && (\n <small className=\"d-block text-muted font-italic pl-4\">{scope.description}</small>\n )}\n </div>\n);\n\nexport default ScopeCheckboxItem;\n"]}
@@ -0,0 +1,12 @@
1
+ interface ScopeFilterBarProps {
2
+ search: string;
3
+ onSearchChange: (value: string) => void;
4
+ selectedOrgs: string[];
5
+ onOrgsChange: (value: string[]) => void;
6
+ contextType: string | undefined;
7
+ contextLabel: string;
8
+ allScopesCount: number;
9
+ totalCount: number;
10
+ }
11
+ declare const ScopeFilterBar: ({ search, onSearchChange, selectedOrgs, onOrgsChange, contextType, contextLabel, allScopesCount, totalCount, }: ScopeFilterBarProps) => import("react").JSX.Element;
12
+ export default ScopeFilterBar;
@@ -0,0 +1,12 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useIntl } from '@openedx/frontend-base';
3
+ import { Form, Icon, Badge, Stack, } from '@openedx/paragon';
4
+ import { Search } from '@openedx/paragon/icons';
5
+ import OrgFilter from '../../components/TableControlBar/OrgFilter';
6
+ import messages from '../messages';
7
+ const ScopeFilterBar = ({ search, onSearchChange, selectedOrgs, onOrgsChange, contextType, contextLabel, allScopesCount, totalCount, }) => {
8
+ const intl = useIntl();
9
+ return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "d-flex align-items-center justify-content-between gap-3 mb-2 flex-wrap", children: [_jsxs("div", { className: "d-flex align-items-center gap-3", children: [_jsx("div", { className: "scope-search-input", children: _jsx(Form.Group, { controlId: "scope-search", className: "mb-0", children: _jsx(Form.Control, { type: "text", value: search, onChange: (e) => onSearchChange(e.target.value), placeholder: intl.formatMessage(messages['wizard.step2.search.placeholder']), trailingElement: _jsx(Icon, { src: Search }) }) }) }), _jsx(OrgFilter, { filterButtonText: intl.formatMessage(messages['wizard.step2.filter.org.label']), filterValue: selectedOrgs, setFilter: onOrgsChange })] }), _jsx("span", { className: "text-muted small text-nowrap", children: intl.formatMessage(messages['wizard.step2.count'], { shown: allScopesCount, total: totalCount }) })] }), contextType && (_jsxs(Stack, { direction: "horizontal", gap: 2, className: "align-items-center mt-3", children: [_jsx("span", { className: "text-muted small", children: intl.formatMessage(messages['wizard.step2.filter.applied']) }), _jsx(Badge, { className: "py-1 px-2", variant: "light", children: contextLabel })] }))] }));
10
+ };
11
+ export default ScopeFilterBar;
12
+ //# sourceMappingURL=ScopeFilterBar.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScopeFilterBar.js","sourceRoot":"","sources":["../../../../src/authz-module/role-assignation-wizard/components/ScopeFilterBar.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EACL,IAAI,EAAE,IAAI,EAAE,KAAK,EAAE,KAAK,GACzB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,MAAM,EAAE,MAAM,wBAAwB,CAAC;AAChD,OAAO,SAAS,MAAM,wDAAwD,CAAC;AAE/E,OAAO,QAAQ,MAAM,oDAAoD,CAAC;AAa1E,MAAM,cAAc,GAAG,CAAC,EACtB,MAAM,EACN,cAAc,EACd,YAAY,EACZ,YAAY,EACZ,WAAW,EACX,YAAY,EACZ,cAAc,EACd,UAAU,GACU,EAAE,EAAE;IACxB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,OAAO,CACL,8BACE,eAAK,SAAS,EAAC,wEAAwE,aACrF,eAAK,SAAS,EAAC,iCAAiC,aAC9C,cAAK,SAAS,EAAC,oBAAoB,YACjC,KAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,cAAc,EAAC,SAAS,EAAC,MAAM,YACnD,KAAC,IAAI,CAAC,OAAO,IACX,IAAI,EAAC,MAAM,EACX,KAAK,EAAE,MAAM,EACb,QAAQ,EAAE,CAAC,CAAgC,EAAE,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,EAC9E,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,iCAAiC,CAAC,CAAC,EAC5E,eAAe,EAAE,KAAC,IAAI,IAAC,GAAG,EAAE,MAAM,GAAI,GACtC,GACS,GACT,EAEN,KAAC,SAAS,IACR,gBAAgB,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,+BAA+B,CAAC,CAAC,EAC/E,WAAW,EAAE,YAAY,EACzB,SAAS,EAAE,YAAY,GACvB,IACE,EAEN,eAAM,SAAS,EAAC,8BAA8B,YAC3C,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,oBAAoB,CAAC,EAAE,EAAE,KAAK,EAAE,cAAc,EAAE,KAAK,EAAE,UAAU,EAAE,CAAC,GAC5F,IACH,EAEL,WAAW,IAAI,CACd,MAAC,KAAK,IAAC,SAAS,EAAC,YAAY,EAAC,GAAG,EAAE,CAAC,EAAE,SAAS,EAAC,yBAAyB,aACvE,eAAM,SAAS,EAAC,kBAAkB,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,6BAA6B,CAAC,CAAC,GAAQ,EACvG,KAAC,KAAK,IAAC,SAAS,EAAC,WAAW,EAAC,OAAO,EAAC,OAAO,YACzC,YAAY,GACP,IACF,CACT,IACA,CACJ,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,cAAc,CAAC","sourcesContent":["import { useIntl } from '@openedx/frontend-base';\nimport {\n Form, Icon, Badge, Stack,\n} from '@openedx/paragon';\nimport { Search } from '@openedx/paragon/icons';\nimport OrgFilter from '@src/authz-module/components/TableControlBar/OrgFilter';\nimport { ChangeEvent } from 'react';\nimport messages from '@src/authz-module/role-assignation-wizard/messages';\n\ninterface ScopeFilterBarProps {\n search: string;\n onSearchChange: (value: string) => void;\n selectedOrgs: string[];\n onOrgsChange: (value: string[]) => void;\n contextType: string | undefined;\n contextLabel: string;\n allScopesCount: number;\n totalCount: number;\n}\n\nconst ScopeFilterBar = ({\n search,\n onSearchChange,\n selectedOrgs,\n onOrgsChange,\n contextType,\n contextLabel,\n allScopesCount,\n totalCount,\n}: ScopeFilterBarProps) => {\n const intl = useIntl();\n\n return (\n <>\n <div className=\"d-flex align-items-center justify-content-between gap-3 mb-2 flex-wrap\">\n <div className=\"d-flex align-items-center gap-3\">\n <div className=\"scope-search-input\">\n <Form.Group controlId=\"scope-search\" className=\"mb-0\">\n <Form.Control\n type=\"text\"\n value={search}\n onChange={(e: ChangeEvent<HTMLInputElement>) => onSearchChange(e.target.value)}\n placeholder={intl.formatMessage(messages['wizard.step2.search.placeholder'])}\n trailingElement={<Icon src={Search} />}\n />\n </Form.Group>\n </div>\n\n <OrgFilter\n filterButtonText={intl.formatMessage(messages['wizard.step2.filter.org.label'])}\n filterValue={selectedOrgs}\n setFilter={onOrgsChange}\n />\n </div>\n\n <span className=\"text-muted small text-nowrap\">\n {intl.formatMessage(messages['wizard.step2.count'], { shown: allScopesCount, total: totalCount })}\n </span>\n </div>\n\n {contextType && (\n <Stack direction=\"horizontal\" gap={2} className=\"align-items-center mt-3\">\n <span className=\"text-muted small\">{intl.formatMessage(messages['wizard.step2.filter.applied'])}</span>\n <Badge className=\"py-1 px-2\" variant=\"light\">\n {contextLabel}\n </Badge>\n </Stack>\n )}\n </>\n );\n};\n\nexport default ScopeFilterBar;\n"]}
@@ -0,0 +1,20 @@
1
+ import { Org, Scope } from '../../../types';
2
+ interface ScopeListQueryState {
3
+ isLoading: boolean;
4
+ isFetchingNextPage: boolean;
5
+ hasNextPage: boolean | undefined;
6
+ isError: boolean;
7
+ fetchNextPage: () => void;
8
+ }
9
+ interface ScopeListProps {
10
+ orderedOrgs: string[];
11
+ scopesByOrg: Record<string, Scope[]>;
12
+ organizations: Org[] | undefined;
13
+ selectedScopes: Set<string>;
14
+ onScopeToggle: (scopeId: string) => void;
15
+ queryState: ScopeListQueryState;
16
+ platformAggregateScopeItem: Scope | null;
17
+ orgAggregateScopeItems: Record<string, Scope>;
18
+ }
19
+ declare const ScopeList: ({ orderedOrgs, scopesByOrg, organizations, selectedScopes, onScopeToggle, queryState, platformAggregateScopeItem, orgAggregateScopeItems, }: ScopeListProps) => import("react").JSX.Element;
20
+ export default ScopeList;
@@ -0,0 +1,30 @@
1
+ import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useEffect, useRef } from 'react';
3
+ import { Spinner } from '@openedx/paragon';
4
+ import { useIntl } from '@openedx/frontend-base';
5
+ import OrgSection from './OrgSection';
6
+ import ScopeCheckboxItem from './ScopeCheckboxItem';
7
+ import messages from '../messages';
8
+ const ScopeList = ({ orderedOrgs, scopesByOrg, organizations, selectedScopes, onScopeToggle, queryState, platformAggregateScopeItem, orgAggregateScopeItems, }) => {
9
+ const intl = useIntl();
10
+ const { isLoading, isFetchingNextPage, hasNextPage, isError, fetchNextPage, } = queryState;
11
+ const loadMoreRef = useRef(null);
12
+ useEffect(() => {
13
+ const el = loadMoreRef.current;
14
+ if (!el)
15
+ return undefined;
16
+ const observer = new IntersectionObserver((entries) => {
17
+ if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage && !isError) {
18
+ fetchNextPage();
19
+ }
20
+ }, { threshold: 0.1 });
21
+ observer.observe(el);
22
+ return () => observer.disconnect();
23
+ }, [hasNextPage, isFetchingNextPage, isError, fetchNextPage]);
24
+ return (_jsx("div", { className: "scope-list border rounded p-3 bg-light-100 overflow-auto", children: isLoading ? (_jsx("div", { className: "d-flex justify-content-center p-4", children: _jsx(Spinner, { animation: "border", screenReaderText: intl.formatMessage(messages['wizard.step2.scopeList.loading']) }) })) : (_jsxs(_Fragment, { children: [platformAggregateScopeItem && (_jsx(ScopeCheckboxItem, { scope: platformAggregateScopeItem, checked: selectedScopes.has(platformAggregateScopeItem.externalKey), onToggle: onScopeToggle })), orderedOrgs.map((org) => {
25
+ var _a, _b;
26
+ return (_jsx(OrgSection, { orgName: ((_a = organizations === null || organizations === void 0 ? void 0 : organizations.find((o) => o.shortName === org)) === null || _a === void 0 ? void 0 : _a.name) || org, scopes: (_b = scopesByOrg[org]) !== null && _b !== void 0 ? _b : [], selectedScopes: selectedScopes, onScopeToggle: onScopeToggle, aggregateScopeItem: orgAggregateScopeItems[org] }, org));
27
+ }), orderedOrgs.length === 0 && !hasNextPage && !isFetchingNextPage && (_jsx("p", { className: "text-muted text-center py-3", children: intl.formatMessage(messages['wizard.step2.scopeList.empty']) })), _jsx("div", { ref: loadMoreRef }), isFetchingNextPage && (_jsx("div", { className: "d-flex justify-content-center py-2", children: _jsx(Spinner, { animation: "border", size: "sm", screenReaderText: intl.formatMessage(messages['wizard.step2.scopeList.loadingMore']) }) }))] })) }));
28
+ };
29
+ export default ScopeList;
30
+ //# sourceMappingURL=ScopeList.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"ScopeList.js","sourceRoot":"","sources":["../../../../src/authz-module/role-assignation-wizard/components/ScopeList.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useEffect, useRef } from 'react';\nimport { Spinner } from '@openedx/paragon';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Org, Scope } from '@src/types';\nimport OrgSection from './OrgSection';\nimport ScopeCheckboxItem from './ScopeCheckboxItem';\nimport messages from '@src/authz-module/role-assignation-wizard/messages';\n\ninterface ScopeListQueryState {\n isLoading: boolean;\n isFetchingNextPage: boolean;\n hasNextPage: boolean | undefined;\n isError: boolean;\n fetchNextPage: () => void;\n}\n\ninterface ScopeListProps {\n orderedOrgs: string[];\n scopesByOrg: Record<string, Scope[]>;\n organizations: Org[] | undefined;\n selectedScopes: Set<string>;\n onScopeToggle: (scopeId: string) => void;\n queryState: ScopeListQueryState;\n platformAggregateScopeItem: Scope | null;\n orgAggregateScopeItems: Record<string, Scope>;\n}\n\nconst ScopeList = ({\n orderedOrgs,\n scopesByOrg,\n organizations,\n selectedScopes,\n onScopeToggle,\n queryState,\n platformAggregateScopeItem,\n orgAggregateScopeItems,\n}: ScopeListProps) => {\n const intl = useIntl();\n const {\n isLoading, isFetchingNextPage, hasNextPage, isError, fetchNextPage,\n } = queryState;\n const loadMoreRef = useRef<HTMLDivElement>(null);\n\n useEffect(() => {\n const el = loadMoreRef.current;\n if (!el) return undefined;\n const observer = new IntersectionObserver(\n (entries) => {\n if (entries[0].isIntersecting && hasNextPage && !isFetchingNextPage && !isError) {\n fetchNextPage();\n }\n },\n { threshold: 0.1 },\n );\n observer.observe(el);\n return () => observer.disconnect();\n }, [hasNextPage, isFetchingNextPage, isError, fetchNextPage]);\n\n return (\n <div className=\"scope-list border rounded p-3 bg-light-100 overflow-auto\">\n {isLoading ? (\n <div className=\"d-flex justify-content-center p-4\">\n <Spinner animation=\"border\" screenReaderText={intl.formatMessage(messages['wizard.step2.scopeList.loading'])} />\n </div>\n ) : (\n <>\n {platformAggregateScopeItem && (\n <ScopeCheckboxItem\n scope={platformAggregateScopeItem}\n checked={selectedScopes.has(platformAggregateScopeItem.externalKey)}\n onToggle={onScopeToggle}\n />\n )}\n\n {orderedOrgs.map((org) => (\n <OrgSection\n key={org}\n orgName={organizations?.find((o) => o.shortName === org)?.name || org}\n scopes={scopesByOrg[org] ?? []}\n selectedScopes={selectedScopes}\n onScopeToggle={onScopeToggle}\n aggregateScopeItem={orgAggregateScopeItems[org]}\n />\n ))}\n\n {/* Loaded pages can be entirely filtered out client-side while later pages\n still hold visible scopes, so only report empty once every page is in. */}\n {orderedOrgs.length === 0 && !hasNextPage && !isFetchingNextPage && (\n <p className=\"text-muted text-center py-3\">{intl.formatMessage(messages['wizard.step2.scopeList.empty'])}</p>\n )}\n\n <div ref={loadMoreRef} />\n\n {isFetchingNextPage && (\n <div className=\"d-flex justify-content-center py-2\">\n <Spinner animation=\"border\" size=\"sm\" screenReaderText={intl.formatMessage(messages['wizard.step2.scopeList.loadingMore'])} />\n </div>\n )}\n </>\n )}\n </div>\n );\n};\n\nexport default ScopeList;\n"]}
@@ -0,0 +1,12 @@
1
+ import { RoleMetadata } from '../../../types';
2
+ interface SelectUsersAndRoleStepProps {
3
+ users: string;
4
+ setUsers: (value: string) => void;
5
+ selectedRole: string | null;
6
+ setSelectedRole: React.Dispatch<React.SetStateAction<string | null>>;
7
+ roles: RoleMetadata[];
8
+ invalidUsers: string[];
9
+ inputRef?: React.RefObject<HTMLTextAreaElement>;
10
+ }
11
+ declare const SelectUsersAndRoleStep: ({ users, setUsers, selectedRole, setSelectedRole, roles, invalidUsers, inputRef, }: SelectUsersAndRoleStepProps) => import("react").JSX.Element;
12
+ export default SelectUsersAndRoleStep;
@@ -0,0 +1,34 @@
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { Form, Stack, OverlayTrigger, Tooltip, } from '@openedx/paragon';
4
+ import { getSiteConfig, useIntl } from '@openedx/frontend-base';
5
+ import HighlightedUsersInput from './HighlightedUsersInput';
6
+ import messages from '../messages';
7
+ const CONTEXT_ORDER = ['library', 'course'];
8
+ const ADMIN_URL = `${getSiteConfig().lmsBaseUrl}/admin`;
9
+ const SelectUsersAndRoleStep = ({ users, setUsers, selectedRole, setSelectedRole, roles, invalidUsers, inputRef, }) => {
10
+ const intl = useIntl();
11
+ const contextLabels = {
12
+ library: intl.formatMessage(messages['wizard.step1.roles.contextLabel.library']),
13
+ course: intl.formatMessage(messages['wizard.step1.roles.contextLabel.course']),
14
+ };
15
+ const rolesByContext = useMemo(() => roles.reduce((acc, role) => {
16
+ const context = role.contextType;
17
+ if (!acc[context])
18
+ acc[context] = [];
19
+ acc[context].push(role);
20
+ return acc;
21
+ }, {}), [roles]);
22
+ const orderedContexts = useMemo(() => CONTEXT_ORDER.filter((ctx) => rolesByContext[ctx]), [rolesByContext]);
23
+ return (_jsxs("div", { className: "select-users-and-role-step", children: [_jsx("h3", { className: "text-primary mb-2", children: intl.formatMessage(messages['wizard.step1.users.heading']) }), _jsxs(Form.Group, { controlId: "users-input", children: [_jsx(Form.Label, { children: intl.formatMessage(messages['wizard.step1.users.label']) }), _jsx(HighlightedUsersInput, { ref: inputRef, id: "users-input", value: users, onChange: setUsers, invalidUsers: invalidUsers, placeholder: intl.formatMessage(messages['wizard.step1.users.placeholder']), hasError: invalidUsers.length > 0, ariaDescribedBy: "users-input-hint" }), _jsx(Form.Text, { id: "users-input-hint", "aria-live": "polite", className: `my-3 ${invalidUsers.length > 0 ? 'text-danger' : ''}`, children: invalidUsers.length > 0
24
+ ? intl.formatMessage(messages['wizard.step1.users.invalid'], { count: invalidUsers.length })
25
+ : intl.formatMessage(messages['wizard.step1.users.hint']) })] }), _jsx("h3", { className: "text-primary mt-4 mb-2", children: intl.formatMessage(messages['wizard.step1.roles.heading']) }), orderedContexts.map((context) => (_jsxs("div", { className: "role-group mb-4", children: [_jsx("h5", { className: "h4 text-gray role-group-header my-4 pl-4", children: contextLabels[context] || context }), _jsx(Form.RadioSet, { name: `role-selection-${context}`, value: selectedRole || '', onChange: (e) => setSelectedRole(e.target.value), "aria-required": true, className: "pl-4", children: (rolesByContext[context] || []).map((role) => {
26
+ const radioContent = (_jsx(Form.Radio, { value: role.role, disabled: role.disabled, children: _jsxs(Stack, { gap: 2, children: [_jsx("span", { className: `role-name ${role.disabled ? ' text-muted' : ''}`, children: role.name }), _jsx("p", { className: "mb-0 text-gray-500 small", children: role.description })] }) }, role.role));
27
+ if (role.disabled) {
28
+ return (_jsx(OverlayTrigger, { placement: "right", overlay: (_jsx(Tooltip, { variant: "light", id: `tooltip-disabled-${role.role}`, children: intl.formatMessage(messages['wizard.step1.roles.disabled.tooltip']) })), children: _jsx("div", { tabIndex: 0, role: "button", "aria-disabled": "true", "aria-describedby": `tooltip-disabled-${role.role}`, children: radioContent }) }, role.role));
29
+ }
30
+ return radioContent;
31
+ }) })] }, context))), _jsxs("div", { className: "mt-3", children: [_jsx("p", { className: "mb-1 font-weight-bold small", children: intl.formatMessage(messages['wizard.step1.docs.heading']) }), _jsxs("p", { className: "mb-0 small", children: [intl.formatMessage(messages['wizard.step1.docs.body']), ' ', _jsx("a", { href: ADMIN_URL, target: "_blank", rel: "noopener noreferrer", children: intl.formatMessage(messages['wizard.step1.docs.link']) })] })] })] }));
32
+ };
33
+ export default SelectUsersAndRoleStep;
34
+ //# sourceMappingURL=SelectUsersAndRoleStep.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"SelectUsersAndRoleStep.js","sourceRoot":"","sources":["../../../../src/authz-module/role-assignation-wizard/components/SelectUsersAndRoleStep.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,OAAO,CAAC;AAChC,OAAO,EACL,IAAI,EAAE,KAAK,EAAE,cAAc,EAC3B,OAAO,GACR,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,aAAa,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AAEhE,OAAO,qBAAqB,MAAM,yBAAyB,CAAC;AAC5D,OAAO,QAAQ,MAAM,oDAAoD,CAAC;AAY1E,MAAM,aAAa,GAAG,CAAC,SAAS,EAAE,QAAQ,CAAC,CAAC;AAC5C,MAAM,SAAS,GAAG,GAAG,aAAa,EAAE,CAAC,UAAU,QAAQ,CAAC;AAExD,MAAM,sBAAsB,GAAG,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,eAAe,EACf,KAAK,EACL,YAAY,EACZ,QAAQ,GACoB,EAAE,EAAE;IAChC,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,MAAM,aAAa,GAA2B;QAC5C,OAAO,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,yCAAyC,CAAC,CAAC;QAChF,MAAM,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,wCAAwC,CAAC,CAAC;KAC/E,CAAC;IAEF,MAAM,cAAc,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,KAAK,CAAC,MAAM,CAAiC,CAAC,GAAG,EAAE,IAAI,EAAE,EAAE;QAC9F,MAAM,OAAO,GAAG,IAAI,CAAC,WAAW,CAAC;QACjC,IAAI,CAAC,GAAG,CAAC,OAAO,CAAC;YAAE,GAAG,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC;QACrC,GAAG,CAAC,OAAO,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QACxB,OAAO,GAAG,CAAC;IACb,CAAC,EAAE,EAAE,CAAC,EAAE,CAAC,KAAK,CAAC,CAAC,CAAC;IAEjB,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,EAAE,CAAC,cAAc,CAAC,GAAG,CAAC,CAAC,EAAE,CAAC,cAAc,CAAC,CAAC,CAAC;IAE5G,OAAO,CACL,eAAK,SAAS,EAAC,4BAA4B,aAEzC,aAAI,SAAS,EAAC,mBAAmB,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,GAAM,EACnG,MAAC,IAAI,CAAC,KAAK,IAAC,SAAS,EAAC,aAAa,aACjC,KAAC,IAAI,CAAC,KAAK,cAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,0BAA0B,CAAC,CAAC,GAAc,EACnF,KAAC,qBAAqB,IACpB,GAAG,EAAE,QAAQ,EACb,EAAE,EAAC,aAAa,EAChB,KAAK,EAAE,KAAK,EACZ,QAAQ,EAAE,QAAQ,EAClB,YAAY,EAAE,YAAY,EAC1B,WAAW,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,gCAAgC,CAAC,CAAC,EAC3E,QAAQ,EAAE,YAAY,CAAC,MAAM,GAAG,CAAC,EACjC,eAAe,EAAC,kBAAkB,GAClC,EACF,KAAC,IAAI,CAAC,IAAI,IAAC,EAAE,EAAC,kBAAkB,eAAW,QAAQ,EAAC,SAAS,EAAE,QAAQ,YAAY,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,YAClH,YAAY,CAAC,MAAM,GAAG,CAAC;4BACtB,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,4BAA4B,CAAC,EAAE,EAAE,KAAK,EAAE,YAAY,CAAC,MAAM,EAAE,CAAC;4BAC5F,CAAC,CAAC,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,yBAAyB,CAAC,CAAC,GACjD,IACD,EAGb,aAAI,SAAS,EAAC,wBAAwB,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,4BAA4B,CAAC,CAAC,GAAM,EACvG,eAAe,CAAC,GAAG,CAAC,CAAC,OAAO,EAAE,EAAE,CAAC,CAChC,eAAmB,SAAS,EAAC,iBAAiB,aAC5C,aAAI,SAAS,EAAC,0CAA0C,YACrD,aAAa,CAAC,OAAO,CAAC,IAAI,OAAO,GAC/B,EACL,KAAC,IAAI,CAAC,QAAQ,IACZ,IAAI,EAAE,kBAAkB,OAAO,EAAE,EACjC,KAAK,EAAE,YAAY,IAAI,EAAE,EACzB,QAAQ,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,eAAe,CAAC,CAAC,CAAC,MAAM,CAAC,KAAK,CAAC,yBAEhD,SAAS,EAAC,MAAM,YAEf,CAAC,cAAc,CAAC,OAAO,CAAC,IAAI,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,IAAI,EAAE,EAAE;4BAC5C,MAAM,YAAY,GAAG,CACnB,KAAC,IAAI,CAAC,KAAK,IAAiB,KAAK,EAAE,IAAI,CAAC,IAAI,EAAE,QAAQ,EAAE,IAAI,CAAC,QAAQ,YACnE,MAAC,KAAK,IAAC,GAAG,EAAE,CAAC,aACX,eAAM,SAAS,EAAE,aAAa,IAAI,CAAC,QAAQ,CAAC,CAAC,CAAC,aAAa,CAAC,CAAC,CAAC,EAAE,EAAE,YAC/D,IAAI,CAAC,IAAI,GACL,EACP,YAAG,SAAS,EAAC,0BAA0B,YAAE,IAAI,CAAC,WAAW,GAAK,IACxD,IANO,IAAI,CAAC,IAAI,CAOb,CACd,CAAC;4BAEF,IAAI,IAAI,CAAC,QAAQ,EAAE,CAAC;gCAClB,OAAO,CACL,KAAC,cAAc,IAEb,SAAS,EAAC,OAAO,EACjB,OAAO,EAAE,CACP,KAAC,OAAO,IAAC,OAAO,EAAC,OAAO,EAAC,EAAE,EAAE,oBAAoB,IAAI,CAAC,IAAI,EAAE,YACzD,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,qCAAqC,CAAC,CAAC,GAC5D,CACX,YAED,cAAK,QAAQ,EAAE,CAAC,EAAE,IAAI,EAAC,QAAQ,mBAAe,MAAM,sBAAmB,oBAAoB,IAAI,CAAC,IAAI,EAAE,YAAG,YAAY,GAAO,IARvH,IAAI,CAAC,IAAI,CASC,CAClB,CAAC;4BACJ,CAAC;4BAED,OAAO,YAAY,CAAC;wBACtB,CAAC,CAAC,GACY,KAzCR,OAAO,CA0CX,CACP,CAAC,EAGF,eAAK,SAAS,EAAC,MAAM,aACnB,YAAG,SAAS,EAAC,6BAA6B,YAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,2BAA2B,CAAC,CAAC,GAAK,EAC1G,aAAG,SAAS,EAAC,YAAY,aACtB,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC,EAAE,GAAG,EAC5D,YAAG,IAAI,EAAE,SAAS,EAAE,MAAM,EAAC,QAAQ,EAAC,GAAG,EAAC,qBAAqB,YAC1D,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,wBAAwB,CAAC,CAAC,GACrD,IACF,IACA,IACF,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,sBAAsB,CAAC","sourcesContent":["import { useMemo } from 'react';\nimport {\n Form, Stack, OverlayTrigger,\n Tooltip,\n} from '@openedx/paragon';\nimport { getSiteConfig, useIntl } from '@openedx/frontend-base';\nimport { RoleMetadata } from '@src/types';\nimport HighlightedUsersInput from './HighlightedUsersInput';\nimport messages from '@src/authz-module/role-assignation-wizard/messages';\n\ninterface SelectUsersAndRoleStepProps {\n users: string;\n setUsers: (value: string) => void;\n selectedRole: string | null;\n setSelectedRole: React.Dispatch<React.SetStateAction<string | null>>;\n roles: RoleMetadata[];\n invalidUsers: string[];\n inputRef?: React.RefObject<HTMLTextAreaElement>;\n}\n\nconst CONTEXT_ORDER = ['library', 'course'];\nconst ADMIN_URL = `${getSiteConfig().lmsBaseUrl}/admin`;\n\nconst SelectUsersAndRoleStep = ({\n users,\n setUsers,\n selectedRole,\n setSelectedRole,\n roles,\n invalidUsers,\n inputRef,\n}: SelectUsersAndRoleStepProps) => {\n const intl = useIntl();\n\n const contextLabels: Record<string, string> = {\n library: intl.formatMessage(messages['wizard.step1.roles.contextLabel.library']),\n course: intl.formatMessage(messages['wizard.step1.roles.contextLabel.course']),\n };\n\n const rolesByContext = useMemo(() => roles.reduce<Record<string, RoleMetadata[]>>((acc, role) => {\n const context = role.contextType;\n if (!acc[context]) acc[context] = [];\n acc[context].push(role);\n return acc;\n }, {}), [roles]);\n\n const orderedContexts = useMemo(() => CONTEXT_ORDER.filter((ctx) => rolesByContext[ctx]), [rolesByContext]);\n\n return (\n <div className=\"select-users-and-role-step\">\n {/* Users Section */}\n <h3 className=\"text-primary mb-2\">{intl.formatMessage(messages['wizard.step1.users.heading'])}</h3>\n <Form.Group controlId=\"users-input\">\n <Form.Label>{intl.formatMessage(messages['wizard.step1.users.label'])}</Form.Label>\n <HighlightedUsersInput\n ref={inputRef}\n id=\"users-input\"\n value={users}\n onChange={setUsers}\n invalidUsers={invalidUsers}\n placeholder={intl.formatMessage(messages['wizard.step1.users.placeholder'])}\n hasError={invalidUsers.length > 0}\n ariaDescribedBy=\"users-input-hint\"\n />\n <Form.Text id=\"users-input-hint\" aria-live=\"polite\" className={`my-3 ${invalidUsers.length > 0 ? 'text-danger' : ''}`}>\n {invalidUsers.length > 0\n ? intl.formatMessage(messages['wizard.step1.users.invalid'], { count: invalidUsers.length })\n : intl.formatMessage(messages['wizard.step1.users.hint'])}\n </Form.Text>\n </Form.Group>\n\n {/* Roles Section */}\n <h3 className=\"text-primary mt-4 mb-2\">{intl.formatMessage(messages['wizard.step1.roles.heading'])}</h3>\n {orderedContexts.map((context) => (\n <div key={context} className=\"role-group mb-4\">\n <h5 className=\"h4 text-gray role-group-header my-4 pl-4\">\n {contextLabels[context] || context}\n </h5>\n <Form.RadioSet\n name={`role-selection-${context}`}\n value={selectedRole || ''}\n onChange={(e) => setSelectedRole(e.target.value)}\n aria-required\n className=\"pl-4\"\n >\n {(rolesByContext[context] || []).map((role) => {\n const radioContent = (\n <Form.Radio key={role.role} value={role.role} disabled={role.disabled}>\n <Stack gap={2}>\n <span className={`role-name ${role.disabled ? ' text-muted' : ''}`}>\n {role.name}\n </span>\n <p className=\"mb-0 text-gray-500 small\">{role.description}</p>\n </Stack>\n </Form.Radio>\n );\n\n if (role.disabled) {\n return (\n <OverlayTrigger\n key={role.role}\n placement=\"right\"\n overlay={(\n <Tooltip variant=\"light\" id={`tooltip-disabled-${role.role}`}>\n {intl.formatMessage(messages['wizard.step1.roles.disabled.tooltip'])}\n </Tooltip>\n )}\n >\n <div tabIndex={0} role=\"button\" aria-disabled=\"true\" aria-describedby={`tooltip-disabled-${role.role}`}>{radioContent}</div>\n </OverlayTrigger>\n );\n }\n\n return radioContent;\n })}\n </Form.RadioSet>\n </div>\n ))}\n\n {/* Documentation Link */}\n <div className=\"mt-3\">\n <p className=\"mb-1 font-weight-bold small\">{intl.formatMessage(messages['wizard.step1.docs.heading'])}</p>\n <p className=\"mb-0 small\">\n {intl.formatMessage(messages['wizard.step1.docs.body'])}{' '}\n <a href={ADMIN_URL} target=\"_blank\" rel=\"noopener noreferrer\">\n {intl.formatMessage(messages['wizard.step1.docs.link'])}\n </a>\n </p>\n </div>\n </div>\n );\n};\n\nexport default SelectUsersAndRoleStep;\n"]}
@@ -0,0 +1,5 @@
1
+ export declare const ROLE_ASSIGNMENT_ERRORS: {
2
+ readonly USER_ALREADY_HAS_ROLE: "user_already_has_role";
3
+ readonly USER_NOT_FOUND: "user_not_found";
4
+ readonly ROLE_ASSIGNMENT_ERROR: "role_assignment_error";
5
+ };
@@ -0,0 +1,6 @@
1
+ export const ROLE_ASSIGNMENT_ERRORS = {
2
+ USER_ALREADY_HAS_ROLE: 'user_already_has_role',
3
+ USER_NOT_FOUND: 'user_not_found',
4
+ ROLE_ASSIGNMENT_ERROR: 'role_assignment_error',
5
+ };
6
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../../src/authz-module/role-assignation-wizard/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,qBAAqB,EAAE,uBAAuB;IAC9C,cAAc,EAAE,gBAAgB;IAChC,qBAAqB,EAAE,uBAAuB;CACtC,CAAC","sourcesContent":["export const ROLE_ASSIGNMENT_ERRORS = {\n USER_ALREADY_HAS_ROLE: 'user_already_has_role',\n USER_NOT_FOUND: 'user_not_found',\n ROLE_ASSIGNMENT_ERROR: 'role_assignment_error',\n} as const;\n"]}
@@ -0,0 +1,23 @@
1
+ import { Scope } from '../../../types';
2
+ interface UseScopeListDataParams {
3
+ contextType: string | undefined;
4
+ search: string;
5
+ orgs: string[];
6
+ }
7
+ declare const useScopeListData: ({ contextType, search, orgs }: UseScopeListDataParams) => {
8
+ organizations: import("../../../types").Org[] | undefined;
9
+ orderedOrgs: string[];
10
+ scopesByOrg: Record<string, Scope[]>;
11
+ allScopes: Scope[];
12
+ totalCount: number;
13
+ queryState: {
14
+ isLoading: boolean;
15
+ isFetchingNextPage: boolean;
16
+ hasNextPage: boolean;
17
+ isError: boolean;
18
+ fetchNextPage: (options?: import("@tanstack/query-core").FetchNextPageOptions) => Promise<import("@tanstack/query-core").InfiniteQueryObserverResult<import("@tanstack/query-core").InfiniteData<import("../../data/api").GetScopesResponse, unknown>, Error>>;
19
+ };
20
+ platformAggregateScopeItem: Scope | null;
21
+ orgAggregateScopeItems: Record<string, Scope>;
22
+ };
23
+ export default useScopeListData;
@@ -0,0 +1,112 @@
1
+ import { useMemo } from 'react';
2
+ import { useIntl } from '@openedx/frontend-base';
3
+ import { useOrgs, useScopes } from '../../data/hooks';
4
+ import { useCourseAuthoringFlag } from '../../hooks/useCourseAuthoringFlag';
5
+ import { getOrgAggregateScopeKey, getPlatformAggregateScopeKey } from '../../constants';
6
+ import messages from '../messages';
7
+ import useScopePermissions from './useScopePermissions';
8
+ const groupByOrg = (acc, scope) => {
9
+ const orgSlug = scope.org.shortName;
10
+ if (!acc[orgSlug])
11
+ acc[orgSlug] = [];
12
+ acc[orgSlug].push(scope);
13
+ return acc;
14
+ };
15
+ const useScopeListData = ({ contextType, search, orgs }) => {
16
+ const intl = useIntl();
17
+ const { data: scopesData, fetchNextPage, hasNextPage, isFetchingNextPage, isLoading, isError, } = useScopes({
18
+ scopeType: contextType,
19
+ search: search || undefined,
20
+ orgs: orgs.length ? orgs : undefined,
21
+ managementPermissionOnly: true,
22
+ });
23
+ const { data: orgsData } = useOrgs();
24
+ const organizations = orgsData === null || orgsData === void 0 ? void 0 : orgsData.results;
25
+ const { isCourseEnabled, isOrgAuthoringEnabled } = useCourseAuthoringFlag();
26
+ const { allScopes, totalCount } = useMemo(() => {
27
+ var _a, _b, _c;
28
+ const scopes = (_a = scopesData === null || scopesData === void 0 ? void 0 : scopesData.pages.flatMap((page) => page.results)) !== null && _a !== void 0 ? _a : [];
29
+ const serverCount = (_c = (_b = scopesData === null || scopesData === void 0 ? void 0 : scopesData.pages[0]) === null || _b === void 0 ? void 0 : _b.count) !== null && _c !== void 0 ? _c : 0;
30
+ // Course scopes are gated by the course-authoring flag; libraries always pass.
31
+ if (contextType !== 'course')
32
+ return { allScopes: scopes, totalCount: serverCount };
33
+ const enabledScopes = scopes.filter((scope) => isCourseEnabled(scope.externalKey));
34
+ // The server count includes authoring-disabled courses this filter hides; subtract
35
+ // the ones already loaded so the reported total converges as pages arrive.
36
+ return {
37
+ allScopes: enabledScopes,
38
+ totalCount: serverCount - (scopes.length - enabledScopes.length),
39
+ };
40
+ }, [scopesData, contextType, isCourseEnabled]);
41
+ const scopesByOrg = useMemo(() => allScopes
42
+ .filter((s) => !!s.org)
43
+ .reduce(groupByOrg, {}), [allScopes]);
44
+ const orderedOrgs = useMemo(() => {
45
+ const orgSlugs = new Set(Object.keys(scopesByOrg));
46
+ // Add orgs from the organizations list (respecting the active org filter)
47
+ organizations === null || organizations === void 0 ? void 0 : organizations.forEach((org) => {
48
+ if (!orgs.length || orgs.includes(org.shortName)) {
49
+ orgSlugs.add(org.shortName);
50
+ }
51
+ });
52
+ return [...orgSlugs].sort();
53
+ }, [scopesByOrg, organizations, orgs]);
54
+ const { hasPlatformPermission, orgHasPermission } = useScopePermissions({ contextType, orderedOrgs });
55
+ const aggregateDescription = contextType === 'course'
56
+ ? intl.formatMessage(messages['wizard.step2.scope.aggregate.description.course'])
57
+ : intl.formatMessage(messages['wizard.step2.scope.aggregate.description.library']);
58
+ const platformAggregateLabel = contextType === 'course'
59
+ ? intl.formatMessage(messages['wizard.step2.scope.aggregate.platform.label.course'])
60
+ : intl.formatMessage(messages['wizard.step2.scope.aggregate.platform.label.library']);
61
+ const orgAggregateLabel = contextType === 'course'
62
+ ? intl.formatMessage(messages['wizard.step2.scopeList.aggregate.label.course'])
63
+ : intl.formatMessage(messages['wizard.step2.scopeList.aggregate.label.library']);
64
+ const platformAggregateScopeItem = (contextType && hasPlatformPermission)
65
+ ? {
66
+ externalKey: getPlatformAggregateScopeKey(contextType),
67
+ displayName: platformAggregateLabel,
68
+ description: aggregateDescription,
69
+ org: null,
70
+ }
71
+ : null;
72
+ /**
73
+ * Builds a map of org-level aggregate scopes keyed by org slug.
74
+ *
75
+ * Each entry represents a wildcard scope that grants access to all courses or
76
+ * libraries within an org (e.g. `course-v1:OrgSlug+*` or `lib:OrgSlug:*`).
77
+ * Only orgs where the current user already holds permission are included.
78
+ *
79
+ * Returns an empty object when `contextType` is not yet defined.
80
+ */
81
+ const orgAggregateScopeItems = useMemo(() => {
82
+ if (!contextType)
83
+ return {};
84
+ return Object.fromEntries(orderedOrgs
85
+ .filter((orgSlug) => orgHasPermission[orgSlug])
86
+ // The org-wide aggregate is only offered when authoring is enabled for that org.
87
+ .filter((orgSlug) => contextType !== 'course' || isOrgAuthoringEnabled(orgSlug))
88
+ .map((orgSlug) => [
89
+ orgSlug,
90
+ {
91
+ externalKey: getOrgAggregateScopeKey(contextType, orgSlug),
92
+ displayName: orgAggregateLabel,
93
+ description: aggregateDescription,
94
+ org: { id: '0', name: orgSlug, shortName: orgSlug },
95
+ },
96
+ ]));
97
+ }, [orderedOrgs, contextType, orgHasPermission, orgAggregateLabel, aggregateDescription, isOrgAuthoringEnabled]);
98
+ return {
99
+ organizations,
100
+ orderedOrgs,
101
+ scopesByOrg,
102
+ allScopes,
103
+ totalCount,
104
+ queryState: {
105
+ isLoading, isFetchingNextPage, hasNextPage, isError, fetchNextPage,
106
+ },
107
+ platformAggregateScopeItem,
108
+ orgAggregateScopeItems,
109
+ };
110
+ };
111
+ export default useScopeListData;
112
+ //# sourceMappingURL=useScopeListData.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useScopeListData.js","sourceRoot":"","sources":["../../../../src/authz-module/role-assignation-wizard/hooks/useScopeListData.ts"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport { Scope } from '@src/types';\nimport { useOrgs, useScopes } from '@src/authz-module/data/hooks';\nimport { useCourseAuthoringFlag } from '@src/authz-module/hooks/useCourseAuthoringFlag';\nimport { getOrgAggregateScopeKey, getPlatformAggregateScopeKey } from '@src/authz-module/constants';\nimport type { ContextType } from '@src/authz-module/constants';\nimport messages from '@src/authz-module/role-assignation-wizard/messages';\nimport useScopePermissions from './useScopePermissions';\n\ninterface UseScopeListDataParams {\n contextType: string | undefined;\n search: string;\n orgs: string[];\n}\n\nconst groupByOrg = (acc: Record<string, Scope[]>, scope: Scope): Record<string, Scope[]> => {\n const orgSlug = scope.org!.shortName;\n if (!acc[orgSlug]) acc[orgSlug] = [];\n acc[orgSlug].push(scope);\n return acc;\n};\n\nconst useScopeListData = ({ contextType, search, orgs }: UseScopeListDataParams) => {\n const intl = useIntl();\n const {\n data: scopesData,\n fetchNextPage,\n hasNextPage,\n isFetchingNextPage,\n isLoading,\n isError,\n } = useScopes({\n scopeType: contextType,\n search: search || undefined,\n orgs: orgs.length ? orgs : undefined,\n managementPermissionOnly: true,\n });\n\n const { data: orgsData } = useOrgs();\n const organizations = orgsData?.results;\n\n const { isCourseEnabled, isOrgAuthoringEnabled } = useCourseAuthoringFlag();\n\n const { allScopes, totalCount } = useMemo(\n () => {\n const scopes = scopesData?.pages.flatMap((page) => page.results) ?? [];\n const serverCount = scopesData?.pages[0]?.count ?? 0;\n // Course scopes are gated by the course-authoring flag; libraries always pass.\n if (contextType !== 'course') return { allScopes: scopes, totalCount: serverCount };\n const enabledScopes = scopes.filter((scope) => isCourseEnabled(scope.externalKey));\n // The server count includes authoring-disabled courses this filter hides; subtract\n // the ones already loaded so the reported total converges as pages arrive.\n return {\n allScopes: enabledScopes,\n totalCount: serverCount - (scopes.length - enabledScopes.length),\n };\n },\n [scopesData, contextType, isCourseEnabled],\n );\n\n const scopesByOrg = useMemo(\n () => allScopes\n .filter((s: Scope) => !!s.org)\n .reduce<Record<string, Scope[]>>(groupByOrg, {}),\n [allScopes],\n );\n\n const orderedOrgs = useMemo(() => {\n const orgSlugs = new Set(Object.keys(scopesByOrg));\n // Add orgs from the organizations list (respecting the active org filter)\n organizations?.forEach((org) => {\n if (!orgs.length || orgs.includes(org.shortName)) {\n orgSlugs.add(org.shortName);\n }\n });\n return [...orgSlugs].sort();\n }, [scopesByOrg, organizations, orgs]);\n\n const { hasPlatformPermission, orgHasPermission } = useScopePermissions({ contextType, orderedOrgs });\n\n const aggregateDescription = contextType === 'course'\n ? intl.formatMessage(messages['wizard.step2.scope.aggregate.description.course'])\n : intl.formatMessage(messages['wizard.step2.scope.aggregate.description.library']);\n\n const platformAggregateLabel = contextType === 'course'\n ? intl.formatMessage(messages['wizard.step2.scope.aggregate.platform.label.course'])\n : intl.formatMessage(messages['wizard.step2.scope.aggregate.platform.label.library']);\n\n const orgAggregateLabel = contextType === 'course'\n ? intl.formatMessage(messages['wizard.step2.scopeList.aggregate.label.course'])\n : intl.formatMessage(messages['wizard.step2.scopeList.aggregate.label.library']);\n\n const platformAggregateScopeItem: Scope | null = (contextType && hasPlatformPermission)\n ? {\n externalKey: getPlatformAggregateScopeKey(contextType as ContextType),\n displayName: platformAggregateLabel,\n description: aggregateDescription,\n org: null,\n }\n : null;\n\n /**\n * Builds a map of org-level aggregate scopes keyed by org slug.\n *\n * Each entry represents a wildcard scope that grants access to all courses or\n * libraries within an org (e.g. `course-v1:OrgSlug+*` or `lib:OrgSlug:*`).\n * Only orgs where the current user already holds permission are included.\n *\n * Returns an empty object when `contextType` is not yet defined.\n */\n const orgAggregateScopeItems = useMemo<Record<string, Scope>>(() => {\n if (!contextType) return {};\n return Object.fromEntries(\n orderedOrgs\n .filter((orgSlug) => orgHasPermission[orgSlug])\n // The org-wide aggregate is only offered when authoring is enabled for that org.\n .filter((orgSlug) => contextType !== 'course' || isOrgAuthoringEnabled(orgSlug))\n .map((orgSlug) => [\n orgSlug,\n {\n externalKey: getOrgAggregateScopeKey(contextType as ContextType, orgSlug),\n displayName: orgAggregateLabel,\n description: aggregateDescription,\n org: { id: '0', name: orgSlug, shortName: orgSlug },\n } satisfies Scope,\n ]),\n );\n }, [orderedOrgs, contextType, orgHasPermission, orgAggregateLabel, aggregateDescription, isOrgAuthoringEnabled]);\n\n return {\n organizations,\n orderedOrgs,\n scopesByOrg,\n allScopes,\n totalCount,\n queryState: {\n isLoading, isFetchingNextPage, hasNextPage, isError, fetchNextPage,\n },\n platformAggregateScopeItem,\n orgAggregateScopeItems,\n };\n};\n\nexport default useScopeListData;\n"]}
@@ -0,0 +1,10 @@
1
+ interface UseScopePermissionsParams {
2
+ contextType: string | undefined;
3
+ orderedOrgs: string[];
4
+ }
5
+ interface UseScopePermissionsResult {
6
+ hasPlatformPermission: boolean;
7
+ orgHasPermission: Record<string, boolean>;
8
+ }
9
+ declare const useScopePermissions: ({ contextType, orderedOrgs, }: UseScopePermissionsParams) => UseScopePermissionsResult;
10
+ export default useScopePermissions;