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

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 (112) hide show
  1. package/dist/authz-module/audit-user/index.js +5 -9
  2. package/dist/authz-module/audit-user/index.js.map +1 -1
  3. package/dist/authz-module/authz-home/index.js +21 -2
  4. package/dist/authz-module/authz-home/index.js.map +1 -1
  5. package/dist/authz-module/components/AddRoleButton.js +1 -1
  6. package/dist/authz-module/components/AddRoleButton.js.map +1 -1
  7. package/dist/authz-module/components/AuthZLayout.d.ts +7 -6
  8. package/dist/authz-module/components/AuthZLayout.js +9 -3
  9. package/dist/authz-module/components/AuthZLayout.js.map +1 -1
  10. package/dist/authz-module/components/AuthZTitle.d.ts +3 -2
  11. package/dist/authz-module/components/AuthZTitle.js +9 -7
  12. package/dist/authz-module/components/AuthZTitle.js.map +1 -1
  13. package/dist/authz-module/components/ExpandableButton.d.ts +17 -0
  14. package/dist/authz-module/components/ExpandableButton.js +16 -0
  15. package/dist/authz-module/components/ExpandableButton.js.map +1 -0
  16. package/dist/authz-module/components/PermissionTable.js +5 -1
  17. package/dist/authz-module/components/PermissionTable.js.map +1 -1
  18. package/dist/authz-module/components/TableCells.d.ts +5 -8
  19. package/dist/authz-module/components/TableCells.js +26 -59
  20. package/dist/authz-module/components/TableCells.js.map +1 -1
  21. package/dist/authz-module/components/TableControlBar/MultipleChoiceFilter.d.ts +1 -1
  22. package/dist/authz-module/components/TableControlBar/MultipleChoiceFilter.js +4 -3
  23. package/dist/authz-module/components/TableControlBar/MultipleChoiceFilter.js.map +1 -1
  24. package/dist/authz-module/components/TableControlBar/OrgFilter.js +2 -2
  25. package/dist/authz-module/components/TableControlBar/OrgFilter.js.map +1 -1
  26. package/dist/authz-module/components/TableControlBar/SearchFilter.d.ts +2 -1
  27. package/dist/authz-module/components/TableControlBar/SearchFilter.js +2 -1
  28. package/dist/authz-module/components/TableControlBar/SearchFilter.js.map +1 -1
  29. package/dist/authz-module/components/TableControlBar/TableControlBar.d.ts +4 -1
  30. package/dist/authz-module/components/TableControlBar/TableControlBar.js +24 -24
  31. package/dist/authz-module/components/TableControlBar/TableControlBar.js.map +1 -1
  32. package/dist/authz-module/components/TableControlBar/types.d.ts +1 -0
  33. package/dist/authz-module/components/TableControlBar/types.js.map +1 -1
  34. package/dist/authz-module/components/TableFooter/TableFooter.d.ts +14 -1
  35. package/dist/authz-module/components/TableFooter/TableFooter.js +3 -2
  36. package/dist/authz-module/components/TableFooter/TableFooter.js.map +1 -1
  37. package/dist/authz-module/components/constants.d.ts +1 -0
  38. package/dist/authz-module/components/constants.js +2 -1
  39. package/dist/authz-module/components/constants.js.map +1 -1
  40. package/dist/authz-module/components/messages.d.ts +0 -5
  41. package/dist/authz-module/components/messages.js +2 -7
  42. package/dist/authz-module/components/messages.js.map +1 -1
  43. package/dist/authz-module/constants.d.ts +18 -8
  44. package/dist/authz-module/constants.js +30 -11
  45. package/dist/authz-module/constants.js.map +1 -1
  46. package/dist/authz-module/data/api.d.ts +12 -4
  47. package/dist/authz-module/data/api.js +11 -2
  48. package/dist/authz-module/data/api.js.map +1 -1
  49. package/dist/authz-module/data/hooks.d.ts +8 -8
  50. package/dist/authz-module/data/hooks.js +15 -20
  51. package/dist/authz-module/data/hooks.js.map +1 -1
  52. package/dist/authz-module/hooks/useExclusiveRowExpansion.d.ts +7 -0
  53. package/dist/authz-module/hooks/useExclusiveRowExpansion.js +24 -0
  54. package/dist/authz-module/hooks/useExclusiveRowExpansion.js.map +1 -0
  55. package/dist/authz-module/index.scss +150 -13
  56. package/dist/authz-module/messages.d.ts +23 -0
  57. package/dist/authz-module/messages.js +18 -0
  58. package/dist/authz-module/messages.js.map +1 -1
  59. package/dist/authz-module/role-assignation-wizard/AssignRoleWizard.js +1 -1
  60. package/dist/authz-module/role-assignation-wizard/AssignRoleWizard.js.map +1 -1
  61. package/dist/authz-module/role-assignation-wizard/AssignRoleWizardPage.js +4 -4
  62. package/dist/authz-module/role-assignation-wizard/AssignRoleWizardPage.js.map +1 -1
  63. package/dist/authz-module/role-assignation-wizard/components/ScopeFilterBar.js +1 -1
  64. package/dist/authz-module/role-assignation-wizard/components/ScopeFilterBar.js.map +1 -1
  65. package/dist/authz-module/role-assignation-wizard/hooks/useScopeListData.js.map +1 -1
  66. package/dist/authz-module/roles-permissions/RolesPermissions.js +3 -3
  67. package/dist/authz-module/roles-permissions/RolesPermissions.js.map +1 -1
  68. package/dist/authz-module/team-members/TeamMembersTable.js +102 -58
  69. package/dist/authz-module/team-members/TeamMembersTable.js.map +1 -1
  70. package/dist/authz-module/team-members/components/AssignedRolesCell.d.ts +20 -0
  71. package/dist/authz-module/team-members/components/AssignedRolesCell.js +54 -0
  72. package/dist/authz-module/team-members/components/AssignedRolesCell.js.map +1 -0
  73. package/dist/authz-module/team-members/components/MoreRolesToggle.d.ts +14 -0
  74. package/dist/authz-module/team-members/components/MoreRolesToggle.js +25 -0
  75. package/dist/authz-module/team-members/components/MoreRolesToggle.js.map +1 -0
  76. package/dist/authz-module/team-members/components/TeamMemberCells.d.ts +17 -0
  77. package/dist/authz-module/team-members/components/TeamMemberCells.js +21 -0
  78. package/dist/authz-module/team-members/components/TeamMemberCells.js.map +1 -0
  79. package/dist/authz-module/team-members/components/TeamMemberViewActionCell.d.ts +18 -0
  80. package/dist/authz-module/team-members/components/TeamMemberViewActionCell.js +42 -0
  81. package/dist/authz-module/team-members/components/TeamMemberViewActionCell.js.map +1 -0
  82. package/dist/authz-module/team-members/components/UserAssignmentsSubTable.d.ts +16 -0
  83. package/dist/authz-module/team-members/components/UserAssignmentsSubTable.js +74 -0
  84. package/dist/authz-module/team-members/components/UserAssignmentsSubTable.js.map +1 -0
  85. package/dist/authz-module/team-members/index.d.ts +1 -0
  86. package/dist/authz-module/team-members/index.js +2 -0
  87. package/dist/authz-module/team-members/index.js.map +1 -0
  88. package/dist/authz-module/team-members/messages.d.ts +56 -1
  89. package/dist/authz-module/team-members/messages.js +59 -4
  90. package/dist/authz-module/team-members/messages.js.map +1 -1
  91. package/dist/authz-module/utils.d.ts +6 -0
  92. package/dist/authz-module/utils.js +12 -10
  93. package/dist/authz-module/utils.js.map +1 -1
  94. package/dist/components/LoadingPage.d.ts +3 -0
  95. package/dist/components/LoadingPage.js +9 -1
  96. package/dist/components/LoadingPage.js.map +1 -1
  97. package/dist/components/messages.d.ts +8 -0
  98. package/dist/components/messages.js +10 -0
  99. package/dist/components/messages.js.map +1 -0
  100. package/dist/index.d.ts +0 -1
  101. package/dist/index.js +0 -1
  102. package/dist/index.js.map +1 -1
  103. package/dist/routes.d.ts +3 -1
  104. package/dist/routes.js +17 -4
  105. package/dist/routes.js.map +1 -1
  106. package/dist/style.scss +19 -2
  107. package/dist/types.d.ts +29 -20
  108. package/dist/types.js.map +1 -1
  109. package/package.json +6 -3
  110. package/dist/authz-module/components/ViewMoreLink.d.ts +0 -8
  111. package/dist/authz-module/components/ViewMoreLink.js +0 -8
  112. package/dist/authz-module/components/ViewMoreLink.js.map +0 -1
@@ -0,0 +1,74 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { useMemo } from 'react';
3
+ import { useIntl } from '@openedx/frontend-base';
4
+ import { Card, DataTable, Icon, TableFooter, } from '@openedx/paragon';
5
+ import { ArrowForward } from '@openedx/paragon/icons';
6
+ import { Link } from 'react-router-dom';
7
+ import { ALL_ORGS_KEY, buildAuditUserPath, getAggregateScopeType, getScopeContextType, } from '../../constants';
8
+ import { getScopeResourceIcon } from '../../utils';
9
+ import componentMessages from '../../components/messages';
10
+ import { RESOURCE_ICONS } from '../../components/constants';
11
+ import { AGGREGATE_SCOPE_LABELS } from '../../messages';
12
+ import messages from '../messages';
13
+ import { RoleBadge } from './AssignedRolesCell';
14
+ // Same badge as the collapsed row above, so the breakdown reads consistently.
15
+ const RoleBadgeCell = ({ row: assignmentRow }) => (_jsx(RoleBadge, { role: assignmentRow.original.role }));
16
+ const ScopeNameCell = ({ row: assignmentRow }) => {
17
+ const { formatMessage } = useIntl();
18
+ const { scope, scopeDisplayName, org } = assignmentRow.original;
19
+ const aggregateType = getAggregateScopeType(scope, org);
20
+ const scopeText = aggregateType
21
+ ? formatMessage(AGGREGATE_SCOPE_LABELS[getScopeContextType(scope)])
22
+ : scopeDisplayName || scope;
23
+ return (_jsxs("span", { className: "d-flex align-items-center", children: [_jsx(Icon, { src: getScopeResourceIcon(scope), className: "mr-2 flex-shrink-0 text-primary", size: "xs" }), _jsx("span", { className: "text-truncate", title: scopeText, children: scopeText })] }));
24
+ };
25
+ // Mirrors ScopeNameCell's icon treatment so all three columns read alike. A wildcard org
26
+ // reaches past any one organization, so it gets the same globe and label as the row above.
27
+ const OrgIconCell = ({ row: assignmentRow }) => {
28
+ const { formatMessage } = useIntl();
29
+ const { org } = assignmentRow.original;
30
+ const isAllOrgs = org === ALL_ORGS_KEY;
31
+ const orgText = isAllOrgs
32
+ ? formatMessage(componentMessages['authz.user.table.org.all.organizations.label'])
33
+ : org;
34
+ return (_jsxs("span", { className: "d-flex align-items-center", children: [_jsx(Icon, { src: isAllOrgs ? RESOURCE_ICONS.GLOBAL : RESOURCE_ICONS.ORGANIZATION, className: "mr-2 flex-shrink-0 text-primary", size: "xs" }), _jsx("span", { className: "text-truncate", title: orgText, children: orgText })] }));
35
+ };
36
+ /**
37
+ * Role breakdown revealed when a team member row is expanded.
38
+ *
39
+ * Lists the assignments the API returned, in order, so the first entry matches the badge
40
+ * still shown in the collapsed row above. The footer total is `assignmentCount`, which
41
+ * may exceed the rows listed here — `View all roles` leads to the audit page when it
42
+ * does.
43
+ */
44
+ const UserAssignmentsSubTable = ({ row }) => {
45
+ const { formatMessage } = useIntl();
46
+ const { assignments = [], assignmentCount, username } = row.original;
47
+ const hasMoreAssignments = assignmentCount > assignments.length;
48
+ const columns = useMemo(() => [
49
+ {
50
+ id: 'role',
51
+ Header: formatMessage(messages['authz.team.members.subtable.column.role.title']),
52
+ accessor: 'role',
53
+ Cell: RoleBadgeCell,
54
+ },
55
+ {
56
+ id: 'scope',
57
+ Header: formatMessage(messages['authz.team.members.subtable.column.scope.title']),
58
+ accessor: 'scope',
59
+ Cell: ScopeNameCell,
60
+ },
61
+ {
62
+ id: 'org',
63
+ Header: formatMessage(messages['authz.team.members.subtable.column.organization.title']),
64
+ accessor: 'org',
65
+ Cell: OrgIconCell,
66
+ },
67
+ ], [formatMessage]);
68
+ return (_jsx(Card, { className: "team-members-table__subtable my-3", children: _jsxs(DataTable, { columns: columns, data: assignments, itemCount: assignments.length, children: [_jsx(DataTable.Table, { isStriped: false }), _jsx(TableFooter, { children: _jsxs("div", { className: "d-flex align-items-center justify-content-center w-100", children: [_jsx("span", { className: "text-gray-500", children: formatMessage(messages['authz.team.members.subtable.showing.text'], {
69
+ shown: String(assignments.length).padStart(2, '0'),
70
+ total: String(assignmentCount).padStart(2, '0'),
71
+ }) }), hasMoreAssignments && (_jsxs(_Fragment, { children: [_jsx("hr", { className: "mx-3" }), _jsxs(Link, { className: "d-inline-flex align-items-center", to: buildAuditUserPath(username), children: [formatMessage(messages['authz.team.members.subtable.view.all.roles']), _jsx(Icon, { src: ArrowForward, size: "xs", className: "ml-1" })] })] }))] }) })] }) }));
72
+ };
73
+ export default UserAssignmentsSubTable;
74
+ //# sourceMappingURL=UserAssignmentsSubTable.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"UserAssignmentsSubTable.js","sourceRoot":"","sources":["../../../../src/authz-module/team-members/components/UserAssignmentsSubTable.tsx"],"names":[],"mappings":"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","sourcesContent":["import { useMemo } from 'react';\nimport { useIntl } from '@openedx/frontend-base';\nimport {\n Card, DataTable, Icon, TableFooter,\n} from '@openedx/paragon';\nimport { ArrowForward } from '@openedx/paragon/icons';\nimport { Link } from 'react-router-dom';\nimport {\n ALL_ORGS_KEY, buildAuditUserPath, getAggregateScopeType, getScopeContextType,\n} from '@src/authz-module/constants';\nimport { getScopeResourceIcon } from '@src/authz-module/utils';\nimport componentMessages from '@src/authz-module/components/messages';\nimport { RESOURCE_ICONS } from '@src/authz-module/components/constants';\nimport type { TeamMember, TeamMemberAssignment } from '@src/types';\nimport { AGGREGATE_SCOPE_LABELS } from '@src/authz-module/messages';\nimport messages from '../messages';\nimport { RoleBadge } from './AssignedRolesCell';\n\ninterface UserAssignmentsSubTableProps {\n row: { original: TeamMember };\n}\n\ninterface AssignmentCellProps {\n row: { original: TeamMemberAssignment };\n}\n\n// Same badge as the collapsed row above, so the breakdown reads consistently.\nconst RoleBadgeCell = ({ row: assignmentRow }: AssignmentCellProps) => (\n <RoleBadge role={assignmentRow.original.role} />\n);\n\nconst ScopeNameCell = ({ row: assignmentRow }: AssignmentCellProps) => {\n const { formatMessage } = useIntl();\n const { scope, scopeDisplayName, org } = assignmentRow.original;\n const aggregateType = getAggregateScopeType(scope, org);\n const scopeText = aggregateType\n ? formatMessage(AGGREGATE_SCOPE_LABELS[getScopeContextType(scope)])\n : scopeDisplayName || scope;\n\n return (\n <span className=\"d-flex align-items-center\">\n <Icon src={getScopeResourceIcon(scope)} className=\"mr-2 flex-shrink-0 text-primary\" size=\"xs\" />\n <span className=\"text-truncate\" title={scopeText}>{scopeText}</span>\n </span>\n );\n};\n\n// Mirrors ScopeNameCell's icon treatment so all three columns read alike. A wildcard org\n// reaches past any one organization, so it gets the same globe and label as the row above.\nconst OrgIconCell = ({ row: assignmentRow }: AssignmentCellProps) => {\n const { formatMessage } = useIntl();\n const { org } = assignmentRow.original;\n const isAllOrgs = org === ALL_ORGS_KEY;\n const orgText = isAllOrgs\n ? formatMessage(componentMessages['authz.user.table.org.all.organizations.label'])\n : org;\n\n return (\n <span className=\"d-flex align-items-center\">\n <Icon src={isAllOrgs ? RESOURCE_ICONS.GLOBAL : RESOURCE_ICONS.ORGANIZATION} className=\"mr-2 flex-shrink-0 text-primary\" size=\"xs\" />\n <span className=\"text-truncate\" title={orgText}>{orgText}</span>\n </span>\n );\n};\n\n/**\n * Role breakdown revealed when a team member row is expanded.\n *\n * Lists the assignments the API returned, in order, so the first entry matches the badge\n * still shown in the collapsed row above. The footer total is `assignmentCount`, which\n * may exceed the rows listed here — `View all roles` leads to the audit page when it\n * does.\n */\nconst UserAssignmentsSubTable = ({ row }: UserAssignmentsSubTableProps) => {\n const { formatMessage } = useIntl();\n const { assignments = [], assignmentCount, username } = row.original;\n const hasMoreAssignments = assignmentCount > assignments.length;\n\n const columns = useMemo(() => [\n {\n id: 'role',\n Header: formatMessage(messages['authz.team.members.subtable.column.role.title']),\n accessor: 'role',\n Cell: RoleBadgeCell,\n },\n {\n id: 'scope',\n Header: formatMessage(messages['authz.team.members.subtable.column.scope.title']),\n accessor: 'scope',\n Cell: ScopeNameCell,\n },\n {\n id: 'org',\n Header: formatMessage(messages['authz.team.members.subtable.column.organization.title']),\n accessor: 'org',\n Cell: OrgIconCell,\n },\n ], [formatMessage]);\n\n return (\n <Card className=\"team-members-table__subtable my-3\">\n <DataTable\n columns={columns}\n data={assignments}\n itemCount={assignments.length}\n >\n <DataTable.Table isStriped={false} />\n <TableFooter>\n <div className=\"d-flex align-items-center justify-content-center w-100\">\n <span className=\"text-gray-500\">\n {formatMessage(messages['authz.team.members.subtable.showing.text'], {\n shown: String(assignments.length).padStart(2, '0'),\n total: String(assignmentCount).padStart(2, '0'),\n })}\n </span>\n {hasMoreAssignments && (\n <>\n {/* Reuses the module's vertical `hr` divider (see index.scss). */}\n <hr className=\"mx-3\" />\n <Link className=\"d-inline-flex align-items-center\" to={buildAuditUserPath(username)}>\n {formatMessage(messages['authz.team.members.subtable.view.all.roles'])}\n <Icon src={ArrowForward} size=\"xs\" className=\"ml-1\" />\n </Link>\n </>\n )}\n </div>\n </TableFooter>\n </DataTable>\n </Card>\n );\n};\n\nexport default UserAssignmentsSubTable;\n"]}
@@ -0,0 +1 @@
1
+ export { default as TeamMembersTable } from './TeamMembersTable';
@@ -0,0 +1,2 @@
1
+ export { default as TeamMembersTable } from './TeamMembersTable';
2
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/authz-module/team-members/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,gBAAgB,EAAE,MAAM,oBAAoB,CAAC","sourcesContent":["export { default as TeamMembersTable } from './TeamMembersTable';\n"]}
@@ -1,5 +1,10 @@
1
1
  declare const messages: {
2
- 'authz.team.members.table.column.name.title': {
2
+ 'authz.team.members.table.column.username.title': {
3
+ id: string;
4
+ defaultMessage: string;
5
+ description: string;
6
+ };
7
+ 'authz.team.members.table.username.current': {
3
8
  id: string;
4
9
  defaultMessage: string;
5
10
  description: string;
@@ -29,5 +34,55 @@ declare const messages: {
29
34
  defaultMessage: string;
30
35
  description: string;
31
36
  };
37
+ 'authz.team.members.table.column.assigned.roles.title': {
38
+ id: string;
39
+ defaultMessage: string;
40
+ description: string;
41
+ };
42
+ 'authz.team.members.table.assigned.roles': {
43
+ id: string;
44
+ defaultMessage: string;
45
+ description: string;
46
+ };
47
+ 'authz.team.members.table.more.roles': {
48
+ id: string;
49
+ defaultMessage: string;
50
+ description: string;
51
+ };
52
+ 'authz.team.members.table.hide.roles': {
53
+ id: string;
54
+ defaultMessage: string;
55
+ description: string;
56
+ };
57
+ 'authz.team.members.subtable.column.role.title': {
58
+ id: string;
59
+ defaultMessage: string;
60
+ description: string;
61
+ };
62
+ 'authz.team.members.subtable.column.scope.title': {
63
+ id: string;
64
+ defaultMessage: string;
65
+ description: string;
66
+ };
67
+ 'authz.team.members.subtable.column.organization.title': {
68
+ id: string;
69
+ defaultMessage: string;
70
+ description: string;
71
+ };
72
+ 'authz.team.members.subtable.showing.text': {
73
+ id: string;
74
+ defaultMessage: string;
75
+ description: string;
76
+ };
77
+ 'authz.team.members.subtable.view.all.roles': {
78
+ id: string;
79
+ defaultMessage: string;
80
+ description: string;
81
+ };
82
+ 'authz.team.members.table.showing.users.text': {
83
+ id: string;
84
+ defaultMessage: string;
85
+ description: string;
86
+ };
32
87
  };
33
88
  export default messages;
@@ -1,9 +1,14 @@
1
1
  import { defineMessages } from '@openedx/frontend-base';
2
2
  const messages = defineMessages({
3
- 'authz.team.members.table.column.name.title': {
4
- id: 'authz.team.members.table.column.name.title',
5
- defaultMessage: 'Name',
6
- description: 'Team members table name column header',
3
+ 'authz.team.members.table.column.username.title': {
4
+ id: 'authz.team.members.table.column.username.title',
5
+ defaultMessage: 'Username',
6
+ description: 'Team members table username column header',
7
+ },
8
+ 'authz.team.members.table.username.current': {
9
+ id: 'authz.team.members.table.username.current',
10
+ defaultMessage: '{username} (Me)',
11
+ description: 'Indicates the current user in the team members table',
7
12
  },
8
13
  'authz.team.members.table.column.email.title': {
9
14
  id: 'authz.team.members.table.column.email.title',
@@ -30,6 +35,56 @@ const messages = defineMessages({
30
35
  defaultMessage: 'Actions',
31
36
  description: 'Team members table actions column header',
32
37
  },
38
+ 'authz.team.members.table.column.assigned.roles.title': {
39
+ id: 'authz.team.members.table.column.assigned.roles.title',
40
+ defaultMessage: 'Assigned roles',
41
+ description: 'Team members table assigned roles column header',
42
+ },
43
+ 'authz.team.members.table.assigned.roles': {
44
+ id: 'authz.team.members.table.assigned.roles',
45
+ defaultMessage: '{role} in {scope}',
46
+ description: 'One role assignment on a team member row, e.g. "Course Admin in Introduction to Data Analysis". {role} is the role badge and {scope} the course or library it applies to, so both the order and the connecting word can be changed per language.',
47
+ },
48
+ 'authz.team.members.table.more.roles': {
49
+ id: 'authz.team.members.table.more.roles',
50
+ defaultMessage: '+{count, plural, one {# more role} other {# more roles}}',
51
+ description: 'Link expanding a team member row to reveal their remaining roles. The count excludes the role already shown in the collapsed row.',
52
+ },
53
+ 'authz.team.members.table.hide.roles': {
54
+ id: 'authz.team.members.table.hide.roles',
55
+ defaultMessage: 'Hide roles',
56
+ description: 'Link collapsing an expanded team member row',
57
+ },
58
+ 'authz.team.members.subtable.column.role.title': {
59
+ id: 'authz.team.members.subtable.column.role.title',
60
+ defaultMessage: 'Role',
61
+ description: 'Role column header of the nested assignments table inside an expanded team member row',
62
+ },
63
+ 'authz.team.members.subtable.column.scope.title': {
64
+ id: 'authz.team.members.subtable.column.scope.title',
65
+ defaultMessage: 'Scope',
66
+ description: 'Scope column header of the nested assignments table inside an expanded team member row',
67
+ },
68
+ 'authz.team.members.subtable.column.organization.title': {
69
+ id: 'authz.team.members.subtable.column.organization.title',
70
+ defaultMessage: 'Organization',
71
+ description: 'Organization column header of the nested assignments table inside an expanded team member row',
72
+ },
73
+ 'authz.team.members.subtable.showing.text': {
74
+ id: 'authz.team.members.subtable.showing.text',
75
+ defaultMessage: 'Showing {shown} of {total}',
76
+ description: 'Footer of the nested assignments table stating how many of the user total number of roles are listed',
77
+ },
78
+ 'authz.team.members.subtable.view.all.roles': {
79
+ id: 'authz.team.members.subtable.view.all.roles',
80
+ defaultMessage: 'View all roles',
81
+ description: 'Link taking the user to the full profile listing every role assigned to a team member',
82
+ },
83
+ 'authz.team.members.table.showing.users.text': {
84
+ id: 'authz.team.members.table.showing.users.text',
85
+ defaultMessage: 'Showing {pageSize, plural, one {# user} other {# users}} of {itemCount}.',
86
+ description: 'Text stating how many team members are listed on the current page out of the total count',
87
+ },
33
88
  });
34
89
  export default messages;
35
90
  //# sourceMappingURL=messages.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/authz-module/team-members/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,4CAA4C,EAAE;QAC5C,EAAE,EAAE,4CAA4C;QAChD,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,uCAAuC;KACrD;IACD,6CAA6C,EAAE;QAC7C,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,OAAO;QACvB,WAAW,EAAE,wCAAwC;KACtD;IACD,oDAAoD,EAAE;QACpD,EAAE,EAAE,oDAAoD;QACxD,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,+CAA+C;KAC7D;IACD,6CAA6C,EAAE;QAC7C,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,OAAO;QACvB,WAAW,EAAE,wCAAwC;KACtD;IACD,4CAA4C,EAAE;QAC5C,EAAE,EAAE,4CAA4C;QAChD,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,uCAAuC;KACrD;IACD,+CAA+C,EAAE;QAC/C,EAAE,EAAE,+CAA+C;QACnD,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,0CAA0C;KACxD;CAEF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n 'authz.team.members.table.column.name.title': {\n id: 'authz.team.members.table.column.name.title',\n defaultMessage: 'Name',\n description: 'Team members table name column header',\n },\n 'authz.team.members.table.column.email.title': {\n id: 'authz.team.members.table.column.email.title',\n defaultMessage: 'Email',\n description: 'Team members table email column header',\n },\n 'authz.team.members.table.column.organization.title': {\n id: 'authz.team.members.table.column.organization.title',\n defaultMessage: 'Organization',\n description: 'Team members table organization column header',\n },\n 'authz.team.members.table.column.scope.title': {\n id: 'authz.team.members.table.column.scope.title',\n defaultMessage: 'Scope',\n description: 'Team members table scope column header',\n },\n 'authz.team.members.table.column.role.title': {\n id: 'authz.team.members.table.column.role.title',\n defaultMessage: 'Role',\n description: 'Team members table role column header',\n },\n 'authz.team.members.table.column.actions.title': {\n id: 'authz.team.members.table.column.actions.title',\n defaultMessage: 'Actions',\n description: 'Team members table actions column header',\n },\n\n});\n\nexport default messages;\n"]}
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../../src/authz-module/team-members/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,gDAAgD,EAAE;QAChD,EAAE,EAAE,gDAAgD;QACpD,cAAc,EAAE,UAAU;QAC1B,WAAW,EAAE,2CAA2C;KACzD;IACD,2CAA2C,EAAE;QAC3C,EAAE,EAAE,2CAA2C;QAC/C,cAAc,EAAE,iBAAiB;QACjC,WAAW,EAAE,sDAAsD;KACpE;IACD,6CAA6C,EAAE;QAC7C,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,OAAO;QACvB,WAAW,EAAE,wCAAwC;KACtD;IACD,oDAAoD,EAAE;QACpD,EAAE,EAAE,oDAAoD;QACxD,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,+CAA+C;KAC7D;IACD,6CAA6C,EAAE;QAC7C,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,OAAO;QACvB,WAAW,EAAE,wCAAwC;KACtD;IACD,4CAA4C,EAAE;QAC5C,EAAE,EAAE,4CAA4C;QAChD,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,uCAAuC;KACrD;IACD,+CAA+C,EAAE;QAC/C,EAAE,EAAE,+CAA+C;QACnD,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,0CAA0C;KACxD;IACD,sDAAsD,EAAE;QACtD,EAAE,EAAE,sDAAsD;QAC1D,cAAc,EAAE,gBAAgB;QAChC,WAAW,EAAE,iDAAiD;KAC/D;IACD,yCAAyC,EAAE;QACzC,EAAE,EAAE,yCAAyC;QAC7C,cAAc,EAAE,mBAAmB;QACnC,WAAW,EAAE,kPAAkP;KAChQ;IACD,qCAAqC,EAAE;QACrC,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,0DAA0D;QAC1E,WAAW,EAAE,mIAAmI;KACjJ;IACD,qCAAqC,EAAE;QACrC,EAAE,EAAE,qCAAqC;QACzC,cAAc,EAAE,YAAY;QAC5B,WAAW,EAAE,6CAA6C;KAC3D;IACD,+CAA+C,EAAE;QAC/C,EAAE,EAAE,+CAA+C;QACnD,cAAc,EAAE,MAAM;QACtB,WAAW,EAAE,uFAAuF;KACrG;IACD,gDAAgD,EAAE;QAChD,EAAE,EAAE,gDAAgD;QACpD,cAAc,EAAE,OAAO;QACvB,WAAW,EAAE,wFAAwF;KACtG;IACD,uDAAuD,EAAE;QACvD,EAAE,EAAE,uDAAuD;QAC3D,cAAc,EAAE,cAAc;QAC9B,WAAW,EAAE,+FAA+F;KAC7G;IACD,0CAA0C,EAAE;QAC1C,EAAE,EAAE,0CAA0C;QAC9C,cAAc,EAAE,4BAA4B;QAC5C,WAAW,EAAE,sGAAsG;KACpH;IACD,4CAA4C,EAAE;QAC5C,EAAE,EAAE,4CAA4C;QAChD,cAAc,EAAE,gBAAgB;QAChC,WAAW,EAAE,uFAAuF;KACrG;IACD,6CAA6C,EAAE;QAC7C,EAAE,EAAE,6CAA6C;QACjD,cAAc,EAAE,0EAA0E;QAC1F,WAAW,EAAE,0FAA0F;KACxG;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n 'authz.team.members.table.column.username.title': {\n id: 'authz.team.members.table.column.username.title',\n defaultMessage: 'Username',\n description: 'Team members table username column header',\n },\n 'authz.team.members.table.username.current': {\n id: 'authz.team.members.table.username.current',\n defaultMessage: '{username} (Me)',\n description: 'Indicates the current user in the team members table',\n },\n 'authz.team.members.table.column.email.title': {\n id: 'authz.team.members.table.column.email.title',\n defaultMessage: 'Email',\n description: 'Team members table email column header',\n },\n 'authz.team.members.table.column.organization.title': {\n id: 'authz.team.members.table.column.organization.title',\n defaultMessage: 'Organization',\n description: 'Team members table organization column header',\n },\n 'authz.team.members.table.column.scope.title': {\n id: 'authz.team.members.table.column.scope.title',\n defaultMessage: 'Scope',\n description: 'Team members table scope column header',\n },\n 'authz.team.members.table.column.role.title': {\n id: 'authz.team.members.table.column.role.title',\n defaultMessage: 'Role',\n description: 'Team members table role column header',\n },\n 'authz.team.members.table.column.actions.title': {\n id: 'authz.team.members.table.column.actions.title',\n defaultMessage: 'Actions',\n description: 'Team members table actions column header',\n },\n 'authz.team.members.table.column.assigned.roles.title': {\n id: 'authz.team.members.table.column.assigned.roles.title',\n defaultMessage: 'Assigned roles',\n description: 'Team members table assigned roles column header',\n },\n 'authz.team.members.table.assigned.roles': {\n id: 'authz.team.members.table.assigned.roles',\n defaultMessage: '{role} in {scope}',\n description: 'One role assignment on a team member row, e.g. \"Course Admin in Introduction to Data Analysis\". {role} is the role badge and {scope} the course or library it applies to, so both the order and the connecting word can be changed per language.',\n },\n 'authz.team.members.table.more.roles': {\n id: 'authz.team.members.table.more.roles',\n defaultMessage: '+{count, plural, one {# more role} other {# more roles}}',\n description: 'Link expanding a team member row to reveal their remaining roles. The count excludes the role already shown in the collapsed row.',\n },\n 'authz.team.members.table.hide.roles': {\n id: 'authz.team.members.table.hide.roles',\n defaultMessage: 'Hide roles',\n description: 'Link collapsing an expanded team member row',\n },\n 'authz.team.members.subtable.column.role.title': {\n id: 'authz.team.members.subtable.column.role.title',\n defaultMessage: 'Role',\n description: 'Role column header of the nested assignments table inside an expanded team member row',\n },\n 'authz.team.members.subtable.column.scope.title': {\n id: 'authz.team.members.subtable.column.scope.title',\n defaultMessage: 'Scope',\n description: 'Scope column header of the nested assignments table inside an expanded team member row',\n },\n 'authz.team.members.subtable.column.organization.title': {\n id: 'authz.team.members.subtable.column.organization.title',\n defaultMessage: 'Organization',\n description: 'Organization column header of the nested assignments table inside an expanded team member row',\n },\n 'authz.team.members.subtable.showing.text': {\n id: 'authz.team.members.subtable.showing.text',\n defaultMessage: 'Showing {shown} of {total}',\n description: 'Footer of the nested assignments table stating how many of the user total number of roles are listed',\n },\n 'authz.team.members.subtable.view.all.roles': {\n id: 'authz.team.members.subtable.view.all.roles',\n defaultMessage: 'View all roles',\n description: 'Link taking the user to the full profile listing every role assigned to a team member',\n },\n 'authz.team.members.table.showing.users.text': {\n id: 'authz.team.members.table.showing.users.text',\n defaultMessage: 'Showing {pageSize, plural, one {# user} other {# users}} of {itemCount}.',\n description: 'Text stating how many team members are listed on the current page out of the total count',\n },\n});\n\nexport default messages;\n"]}
@@ -17,8 +17,14 @@ export declare const getCellHeader: (columnId: string, columnTitle: string, filt
17
17
  displayName: string;
18
18
  toString(): string;
19
19
  };
20
+ /**
21
+ * Picks the course or library icon for a scope from the scope key itself.
22
+ */
23
+ export declare const getScopeResourceIcon: (scope: string) => any;
24
+ /** Unrecognised scopes fall back to the course permission, as `getScopeContextType` does. */
20
25
  export declare const getScopeManageAction: (scope: string) => string;
21
26
  export declare const getScopeManageActionPermission: (scope: string) => {
22
27
  action: string;
23
28
  scope: string;
24
29
  };
30
+ export declare const validateSizeFormControl: (size: any) => any;
@@ -1,6 +1,8 @@
1
1
  import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { Icon } from '@openedx/paragon';
3
3
  import { FilterList } from '@openedx/paragon/icons';
4
+ import { RESOURCE_ICONS } from './components/constants';
5
+ import { CONTEXT_TYPES, getScopeContextType } from './constants';
4
6
  import { CONTENT_COURSE_PERMISSIONS, CONTENT_LIBRARY_PERMISSIONS } from './roles-permissions';
5
7
  /**
6
8
  * Returns a header value for a DataTable column that shows a filter icon
@@ -25,16 +27,14 @@ export const getCellHeader = (columnId, columnTitle, filtersApplied) => {
25
27
  }
26
28
  return columnTitle;
27
29
  };
28
- export const getScopeManageAction = (scope) => {
29
- if (scope.startsWith('lib')) {
30
- return CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TEAM;
31
- }
32
- if (scope.startsWith('course')) {
33
- return CONTENT_COURSE_PERMISSIONS.MANAGE_COURSE_TEAM;
34
- }
35
- // Default fallback or throw error for unknown scopes
36
- return CONTENT_COURSE_PERMISSIONS.MANAGE_COURSE_TEAM;
37
- };
30
+ /**
31
+ * Picks the course or library icon for a scope from the scope key itself.
32
+ */
33
+ export const getScopeResourceIcon = (scope) => (getScopeContextType(scope) === CONTEXT_TYPES.LIBRARY ? RESOURCE_ICONS.LIBRARY : RESOURCE_ICONS.COURSE);
34
+ /** Unrecognised scopes fall back to the course permission, as `getScopeContextType` does. */
35
+ export const getScopeManageAction = (scope) => (getScopeContextType(scope) === CONTEXT_TYPES.LIBRARY
36
+ ? CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TEAM
37
+ : CONTENT_COURSE_PERMISSIONS.MANAGE_COURSE_TEAM);
38
38
  export const getScopeManageActionPermission = (scope) => {
39
39
  const action = getScopeManageAction(scope);
40
40
  return {
@@ -42,4 +42,6 @@ export const getScopeManageActionPermission = (scope) => {
42
42
  scope,
43
43
  };
44
44
  };
45
+ // "md" is mapped to undefined because Paragon's Form.Control has no "md" size.
46
+ export const validateSizeFormControl = (size) => size === 'md' ? undefined : size;
45
47
  //# sourceMappingURL=utils.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/authz-module/utils.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAE9F;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAE,WAAmB,EAAE,cAAwB,EAAE,EAAE;IAC/F,IAAI,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtC,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,CAC3B,gBAAM,SAAS,EAAC,oCAAoC,aAClD,KAAC,IAAI,IAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,MAAM,GAAG,EACnD,WAAW,IACP,CACR,CAAC;QACF,cAAc,CAAC,WAAW,GAAG,kBAAkB,QAAQ,EAAE,CAAC;QAC1D,cAAc,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,kBAAkB,QAAQ,EAAE,CAAC;QAC7D,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAa,EAAE,EAAE;IACpD,IAAI,KAAK,CAAC,UAAU,CAAC,KAAK,CAAC,EAAE,CAAC;QAC5B,OAAO,2BAA2B,CAAC,mBAAmB,CAAC;IACzD,CAAC;IACD,IAAI,KAAK,CAAC,UAAU,CAAC,QAAQ,CAAC,EAAE,CAAC;QAC/B,OAAO,0BAA0B,CAAC,kBAAkB,CAAC;IACvD,CAAC;IACD,qDAAqD;IACrD,OAAO,0BAA0B,CAAC,kBAAkB,CAAC;AACvD,CAAC,CAAC;AAEF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,KAAa,EAAE,EAAE;IAC9D,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC3C,OAAO;QACL,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { Icon } from '@openedx/paragon';\nimport { FilterList } from '@openedx/paragon/icons';\nimport { CONTENT_COURSE_PERMISSIONS, CONTENT_LIBRARY_PERMISSIONS } from './roles-permissions';\n\n/**\n * Returns a header value for a DataTable column that shows a filter icon\n * when the column has an active filter.\n *\n * When a filter is active, returns a **component function** with a custom\n * toString() override. This is necessary because Paragon's TableRow builds\n * cell keys via: `${cell.column.Header}${row.id}`\n *\n * - A JSX element stringifies to \"[object Object]\", causing duplicate keys.\n * - A function with a custom toString() produces a unique string per column.\n *\n * react-table's render('Header') calls the function as a component, so the\n * JSX is still rendered correctly in the table header.\n */\nexport const getCellHeader = (columnId: string, columnTitle: string, filtersApplied: string[]) => {\n if (filtersApplied.includes(columnId)) {\n const FilteredHeader = () => (\n <span className=\"d-flex flex-row align-items-center\">\n <Icon src={FilterList} size=\"sm\" className=\"mr-2\" />\n {columnTitle}\n </span>\n );\n FilteredHeader.displayName = `FilteredHeader_${columnId}`;\n FilteredHeader.toString = () => `FilteredHeader_${columnId}`;\n return FilteredHeader;\n }\n return columnTitle;\n};\n\nexport const getScopeManageAction = (scope: string) => {\n if (scope.startsWith('lib')) {\n return CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TEAM;\n }\n if (scope.startsWith('course')) {\n return CONTENT_COURSE_PERMISSIONS.MANAGE_COURSE_TEAM;\n }\n // Default fallback or throw error for unknown scopes\n return CONTENT_COURSE_PERMISSIONS.MANAGE_COURSE_TEAM;\n};\n\nexport const getScopeManageActionPermission = (scope: string) => {\n const action = getScopeManageAction(scope);\n return {\n action,\n scope,\n };\n};\n"]}
1
+ {"version":3,"file":"utils.js","sourceRoot":"","sources":["../../src/authz-module/utils.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,kBAAkB,CAAC;AACxC,OAAO,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAC;AACpD,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AACxD,OAAO,EAAE,aAAa,EAAE,mBAAmB,EAAE,MAAM,aAAa,CAAC;AACjE,OAAO,EAAE,0BAA0B,EAAE,2BAA2B,EAAE,MAAM,qBAAqB,CAAC;AAE9F;;;;;;;;;;;;;GAaG;AACH,MAAM,CAAC,MAAM,aAAa,GAAG,CAAC,QAAgB,EAAE,WAAmB,EAAE,cAAwB,EAAE,EAAE;IAC/F,IAAI,cAAc,CAAC,QAAQ,CAAC,QAAQ,CAAC,EAAE,CAAC;QACtC,MAAM,cAAc,GAAG,GAAG,EAAE,CAAC,CAC3B,gBAAM,SAAS,EAAC,oCAAoC,aAClD,KAAC,IAAI,IAAC,GAAG,EAAE,UAAU,EAAE,IAAI,EAAC,IAAI,EAAC,SAAS,EAAC,MAAM,GAAG,EACnD,WAAW,IACP,CACR,CAAC;QACF,cAAc,CAAC,WAAW,GAAG,kBAAkB,QAAQ,EAAE,CAAC;QAC1D,cAAc,CAAC,QAAQ,GAAG,GAAG,EAAE,CAAC,kBAAkB,QAAQ,EAAE,CAAC;QAC7D,OAAO,cAAc,CAAC;IACxB,CAAC;IACD,OAAO,WAAW,CAAC;AACrB,CAAC,CAAC;AAEF;;GAEG;AACH,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CACrD,mBAAmB,CAAC,KAAK,CAAC,KAAK,aAAa,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,OAAO,CAAC,CAAC,CAAC,cAAc,CAAC,MAAM,CACtG,CAAC;AAEF,6FAA6F;AAC7F,MAAM,CAAC,MAAM,oBAAoB,GAAG,CAAC,KAAa,EAAE,EAAE,CAAC,CACrD,mBAAmB,CAAC,KAAK,CAAC,KAAK,aAAa,CAAC,OAAO;IAClD,CAAC,CAAC,2BAA2B,CAAC,mBAAmB;IACjD,CAAC,CAAC,0BAA0B,CAAC,kBAAkB,CAClD,CAAC;AAEF,MAAM,CAAC,MAAM,8BAA8B,GAAG,CAAC,KAAa,EAAE,EAAE;IAC9D,MAAM,MAAM,GAAG,oBAAoB,CAAC,KAAK,CAAC,CAAC;IAC3C,OAAO;QACL,MAAM;QACN,KAAK;KACN,CAAC;AACJ,CAAC,CAAC;AAEF,+EAA+E;AAC/E,MAAM,CAAC,MAAM,uBAAuB,GAAG,CAAC,IAAI,EAAE,EAAE,CAC9C,IAAI,KAAK,IAAI,CAAC,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,IAAI,CAAC","sourcesContent":["import { Icon } from '@openedx/paragon';\nimport { FilterList } from '@openedx/paragon/icons';\nimport { RESOURCE_ICONS } from './components/constants';\nimport { CONTEXT_TYPES, getScopeContextType } from './constants';\nimport { CONTENT_COURSE_PERMISSIONS, CONTENT_LIBRARY_PERMISSIONS } from './roles-permissions';\n\n/**\n * Returns a header value for a DataTable column that shows a filter icon\n * when the column has an active filter.\n *\n * When a filter is active, returns a **component function** with a custom\n * toString() override. This is necessary because Paragon's TableRow builds\n * cell keys via: `${cell.column.Header}${row.id}`\n *\n * - A JSX element stringifies to \"[object Object]\", causing duplicate keys.\n * - A function with a custom toString() produces a unique string per column.\n *\n * react-table's render('Header') calls the function as a component, so the\n * JSX is still rendered correctly in the table header.\n */\nexport const getCellHeader = (columnId: string, columnTitle: string, filtersApplied: string[]) => {\n if (filtersApplied.includes(columnId)) {\n const FilteredHeader = () => (\n <span className=\"d-flex flex-row align-items-center\">\n <Icon src={FilterList} size=\"sm\" className=\"mr-2\" />\n {columnTitle}\n </span>\n );\n FilteredHeader.displayName = `FilteredHeader_${columnId}`;\n FilteredHeader.toString = () => `FilteredHeader_${columnId}`;\n return FilteredHeader;\n }\n return columnTitle;\n};\n\n/**\n * Picks the course or library icon for a scope from the scope key itself.\n */\nexport const getScopeResourceIcon = (scope: string) => (\n getScopeContextType(scope) === CONTEXT_TYPES.LIBRARY ? RESOURCE_ICONS.LIBRARY : RESOURCE_ICONS.COURSE\n);\n\n/** Unrecognised scopes fall back to the course permission, as `getScopeContextType` does. */\nexport const getScopeManageAction = (scope: string) => (\n getScopeContextType(scope) === CONTEXT_TYPES.LIBRARY\n ? CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TEAM\n : CONTENT_COURSE_PERMISSIONS.MANAGE_COURSE_TEAM\n);\n\nexport const getScopeManageActionPermission = (scope: string) => {\n const action = getScopeManageAction(scope);\n return {\n action,\n scope,\n };\n};\n\n// \"md\" is mapped to undefined because Paragon's Form.Control has no \"md\" size.\nexport const validateSizeFormControl = (size) =>\n size === 'md' ? undefined : size;\n"]}
@@ -1,2 +1,5 @@
1
+ /**
2
+ * Full-page loading state.
3
+ */
1
4
  declare const LoadingPage: () => import("react").JSX.Element;
2
5
  export default LoadingPage;
@@ -1,5 +1,13 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
+ import { useIntl } from '@openedx/frontend-base';
2
3
  import { Spinner, Container } from '@openedx/paragon';
3
- const LoadingPage = () => (_jsx(Container, { className: "d-flex vh-100", children: _jsx(Spinner, { variant: "primary", animation: "border", role: "status", className: "mb-3 m-auto" }) }));
4
+ import messages from './messages';
5
+ /**
6
+ * Full-page loading state.
7
+ */
8
+ const LoadingPage = () => {
9
+ const intl = useIntl();
10
+ return (_jsx(Container, { className: "d-flex vh-100", children: _jsx(Spinner, { variant: "primary", animation: "border", screenReaderText: intl.formatMessage(messages['loading.page.screenreader.text']), className: "mb-3 m-auto" }) }));
11
+ };
4
12
  export default LoadingPage;
5
13
  //# sourceMappingURL=LoadingPage.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"LoadingPage.js","sourceRoot":"","sources":["../../src/components/LoadingPage.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AAEtD,MAAM,WAAW,GAAG,GAAG,EAAE,CAAC,CACxB,KAAC,SAAS,IAAC,SAAS,EAAC,eAAe,YAClC,KAAC,OAAO,IACN,OAAO,EAAC,SAAS,EACjB,SAAS,EAAC,QAAQ,EAClB,IAAI,EAAC,QAAQ,EACb,SAAS,EAAC,aAAa,GACvB,GACQ,CACb,CAAC;AAEF,eAAe,WAAW,CAAC","sourcesContent":["import { Spinner, Container } from '@openedx/paragon';\n\nconst LoadingPage = () => (\n <Container className=\"d-flex vh-100\">\n <Spinner\n variant=\"primary\"\n animation=\"border\"\n role=\"status\"\n className=\"mb-3 m-auto\"\n />\n </Container>\n);\n\nexport default LoadingPage;\n"]}
1
+ {"version":3,"file":"LoadingPage.js","sourceRoot":"","sources":["../../src/components/LoadingPage.tsx"],"names":[],"mappings":";AAAA,OAAO,EAAE,OAAO,EAAE,MAAM,wBAAwB,CAAC;AACjD,OAAO,EAAE,OAAO,EAAE,SAAS,EAAE,MAAM,kBAAkB,CAAC;AACtD,OAAO,QAAQ,MAAM,YAAY,CAAC;AAElC;;GAEG;AACH,MAAM,WAAW,GAAG,GAAG,EAAE;IACvB,MAAM,IAAI,GAAG,OAAO,EAAE,CAAC;IAEvB,OAAO,CACL,KAAC,SAAS,IAAC,SAAS,EAAC,eAAe,YAClC,KAAC,OAAO,IACN,OAAO,EAAC,SAAS,EACjB,SAAS,EAAC,QAAQ,EAClB,gBAAgB,EAAE,IAAI,CAAC,aAAa,CAAC,QAAQ,CAAC,gCAAgC,CAAC,CAAC,EAChF,SAAS,EAAC,aAAa,GACvB,GACQ,CACb,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,WAAW,CAAC","sourcesContent":["import { useIntl } from '@openedx/frontend-base';\nimport { Spinner, Container } from '@openedx/paragon';\nimport messages from './messages';\n\n/**\n * Full-page loading state.\n */\nconst LoadingPage = () => {\n const intl = useIntl();\n\n return (\n <Container className=\"d-flex vh-100\">\n <Spinner\n variant=\"primary\"\n animation=\"border\"\n screenReaderText={intl.formatMessage(messages['loading.page.screenreader.text'])}\n className=\"mb-3 m-auto\"\n />\n </Container>\n );\n};\n\nexport default LoadingPage;\n"]}
@@ -0,0 +1,8 @@
1
+ declare const messages: {
2
+ 'loading.page.screenreader.text': {
3
+ id: string;
4
+ defaultMessage: string;
5
+ description: string;
6
+ };
7
+ };
8
+ export default messages;
@@ -0,0 +1,10 @@
1
+ import { defineMessages } from '@openedx/frontend-base';
2
+ const messages = defineMessages({
3
+ 'loading.page.screenreader.text': {
4
+ id: 'loading.page.screenreader.text',
5
+ defaultMessage: 'Loading',
6
+ description: 'Announced by screen readers while a full-page loading spinner is shown.',
7
+ },
8
+ });
9
+ export default messages;
10
+ //# sourceMappingURL=messages.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/components/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAAC;IAC9B,gCAAgC,EAAE;QAChC,EAAE,EAAE,gCAAgC;QACpC,cAAc,EAAE,SAAS;QACzB,WAAW,EAAE,yEAAyE;KACvF;CACF,CAAC,CAAC;AAEH,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages({\n 'loading.page.screenreader.text': {\n id: 'loading.page.screenreader.text',\n defaultMessage: 'Loading',\n description: 'Announced by screen readers while a full-page loading spinner is shown.',\n },\n});\n\nexport default messages;\n"]}
package/dist/index.d.ts CHANGED
@@ -1,3 +1,2 @@
1
1
  export { default as adminConsoleApp } from './app';
2
2
  export { default as adminConsoleRoutes } from './routes';
3
- export { default as adminConsoleMessages } from './i18n';
package/dist/index.js CHANGED
@@ -1,4 +1,3 @@
1
1
  export { default as adminConsoleApp } from './app';
2
2
  export { default as adminConsoleRoutes } from './routes';
3
- export { default as adminConsoleMessages } from './i18n';
4
3
  //# sourceMappingURL=index.js.map
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,UAAU,CAAC;AACzD,OAAO,EAAE,OAAO,IAAI,oBAAoB,EAAE,MAAM,QAAQ,CAAC","sourcesContent":["export { default as adminConsoleApp } from './app';\nexport { default as adminConsoleRoutes } from './routes';\nexport { default as adminConsoleMessages } from './i18n';\n"]}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,OAAO,IAAI,eAAe,EAAE,MAAM,OAAO,CAAC;AACnD,OAAO,EAAE,OAAO,IAAI,kBAAkB,EAAE,MAAM,UAAU,CAAC","sourcesContent":["export { default as adminConsoleApp } from './app';\nexport { default as adminConsoleRoutes } from './routes';\n"]}
package/dist/routes.d.ts CHANGED
@@ -3,7 +3,9 @@ declare const routes: {
3
3
  id: string;
4
4
  path: string;
5
5
  loader: typeof authenticatedLoader;
6
- Component: import("react").LazyExoticComponent<() => import("react").JSX.Element>;
6
+ lazy(): Promise<{
7
+ Component: () => import("react").JSX.Element;
8
+ }>;
7
9
  handle: {
8
10
  roles: string[];
9
11
  };
package/dist/routes.js CHANGED
@@ -1,12 +1,25 @@
1
- import { lazy } from 'react';
1
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
+ return new (P || (P = Promise))(function (resolve, reject) {
4
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
7
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
8
+ });
9
+ };
2
10
  import { authenticatedLoader } from '@openedx/frontend-base';
3
- const Main = lazy(() => import('./Main'));
11
+ import { AUTHZ_HOME_PATH } from './authz-module/constants';
4
12
  const routes = [
5
13
  {
6
14
  id: 'org.openedx.frontend.route.adminConsole.main',
7
- path: '/authz/*',
15
+ path: `${AUTHZ_HOME_PATH}/*`,
8
16
  loader: authenticatedLoader,
9
- Component: Main,
17
+ lazy() {
18
+ return __awaiter(this, void 0, void 0, function* () {
19
+ const { default: Main } = yield import('./Main');
20
+ return { Component: Main };
21
+ });
22
+ },
10
23
  handle: {
11
24
  roles: ['org.openedx.frontend.role.adminConsole'],
12
25
  },
@@ -1 +1 @@
1
- {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAE,MAAM,OAAO,CAAC;AAC7B,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;AAE1C,MAAM,MAAM,GAAG;IACb;QACE,EAAE,EAAE,8CAA8C;QAClD,IAAI,EAAE,UAAU;QAChB,MAAM,EAAE,mBAAmB;QAC3B,SAAS,EAAE,IAAI;QACf,MAAM,EAAE;YACN,KAAK,EAAE,CAAC,wCAAwC,CAAC;SAClD;KACF;CACF,CAAC;AAEF,eAAe,MAAM,CAAC","sourcesContent":["import { lazy } from 'react';\nimport { authenticatedLoader } from '@openedx/frontend-base';\n\nconst Main = lazy(() => import('./Main'));\n\nconst routes = [\n {\n id: 'org.openedx.frontend.route.adminConsole.main',\n path: '/authz/*',\n loader: authenticatedLoader,\n Component: Main,\n handle: {\n roles: ['org.openedx.frontend.role.adminConsole'],\n },\n },\n];\n\nexport default routes;\n"]}
1
+ {"version":3,"file":"routes.js","sourceRoot":"","sources":["../src/routes.tsx"],"names":[],"mappings":";;;;;;;;;AAAA,OAAO,EAAE,mBAAmB,EAAE,MAAM,wBAAwB,CAAC;AAE7D,OAAO,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAC;AAE3D,MAAM,MAAM,GAAG;IACb;QACE,EAAE,EAAE,8CAA8C;QAClD,IAAI,EAAE,GAAG,eAAe,IAAI;QAC5B,MAAM,EAAE,mBAAmB;QACrB,IAAI;;gBACR,MAAM,EAAE,OAAO,EAAE,IAAI,EAAE,GAAG,MAAM,MAAM,CAAC,QAAQ,CAAC,CAAC;gBACjD,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,CAAC;YAC7B,CAAC;SAAA;QACD,MAAM,EAAE;YACN,KAAK,EAAE,CAAC,wCAAwC,CAAC;SAClD;KACF;CACF,CAAC;AAEF,eAAe,MAAM,CAAC","sourcesContent":["import { authenticatedLoader } from '@openedx/frontend-base';\n\nimport { AUTHZ_HOME_PATH } from './authz-module/constants';\n\nconst routes = [\n {\n id: 'org.openedx.frontend.route.adminConsole.main',\n path: `${AUTHZ_HOME_PATH}/*`,\n loader: authenticatedLoader,\n async lazy() {\n const { default: Main } = await import('./Main');\n return { Component: Main };\n },\n handle: {\n roles: ['org.openedx.frontend.role.adminConsole'],\n },\n },\n];\n\nexport default routes;\n"]}
package/dist/style.scss CHANGED
@@ -1,5 +1,22 @@
1
- // App-scoped runtime styles for the toast manager. Global selectors, so
2
- // they must not live under any component's scoped stylesheet.
1
+ // App-scoped global styles. Global selectors, so they must not live under any
2
+ // component's scoped stylesheet.
3
+
4
+ // Lines content up with the site header (`Container fluid size="xl"`) while the element
5
+ // itself stays full width, so a background it carries still bleeds to the edges.
6
+ // The overhang is measured from the element's own width, so it has to span the viewport:
7
+ // inside a constrained box it collapses to zero. Use `py-*` for vertical padding; `p-*`
8
+ // sets the `padding` shorthand `!important`, which wipes out the horizontal inset.
9
+ .page-band {
10
+ --page-band-overhang: max(
11
+ 0px,
12
+ (100% - var(--pgn-size-container-max-width-xl) - var(--pgn-spacing-grid-gutter-width)) / 2
13
+ );
14
+
15
+ padding-left: calc(var(--page-band-overhang) + var(--pgn-spacing-spacer-5));
16
+ padding-right: calc(var(--page-band-overhang) + var(--pgn-spacing-spacer-5));
17
+ }
18
+
19
+ // Runtime styles for the toast manager.
3
20
  .toast-container {
4
21
  // Ensure toast appears above modal
5
22
  z-index: 1000;
package/dist/types.d.ts CHANGED
@@ -5,26 +5,6 @@ export interface PermissionValidationRequest {
5
5
  export interface PermissionValidationResponse extends PermissionValidationRequest {
6
6
  allowed: boolean;
7
7
  }
8
- export interface TeamMember {
9
- username: string;
10
- fullName: string;
11
- email: string;
12
- roles: string[];
13
- createdAt: string;
14
- scope: {
15
- resource: string;
16
- type: 'COURSE' | 'LIBRARY' | 'GLOBAL';
17
- };
18
- organization: string;
19
- role: string;
20
- }
21
- export interface LibraryMetadata {
22
- id: string;
23
- org: string;
24
- title: string;
25
- slug: string;
26
- allowPublicRead: boolean;
27
- }
28
8
  export interface RoleMetadata {
29
9
  role: string;
30
10
  name: string;
@@ -91,3 +71,32 @@ export interface RoleToDelete {
91
71
  export type UserRoleWithPermissions = UserRole & {
92
72
  canManageScope?: boolean;
93
73
  };
74
+ /**
75
+ * A single role assignment nested under a team member in the user-grouped listing.
76
+ * Mirrors `results[].assignments[]` of `GET /api/authz/v1/users/`.
77
+ */
78
+ export interface TeamMemberAssignment {
79
+ role: string;
80
+ org: string;
81
+ /** Scope external key (e.g. `course-v1:Org+Course+Run`). Identifies the scope and is
82
+ * what the scope filter sends back to the API — not shown to the user. */
83
+ scope: string;
84
+ /** Human-readable scope name shown in the table. The API returns an empty string for
85
+ * glob scopes and for scopes whose backing course or library no longer exists, so
86
+ * callers fall back to `scope`. */
87
+ scopeDisplayName: string;
88
+ permissionCount: number;
89
+ }
90
+ /**
91
+ * A team member as returned by the user-grouped assignments endpoint: one entry per
92
+ * user, carrying the first few of their assignments. `assignmentCount` counts every
93
+ * assignment behind that slice, so it can exceed `assignments.length`. Both are scoped
94
+ * to the active role, org and scope filters and to what the caller may view.
95
+ */
96
+ export interface TeamMember {
97
+ username: string;
98
+ fullName: string;
99
+ email: string;
100
+ assignmentCount: number;
101
+ assignments: TeamMemberAssignment[];
102
+ }
package/dist/types.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface PermissionValidationRequest {\n action: string;\n scope?: string;\n}\n\nexport interface PermissionValidationResponse extends PermissionValidationRequest {\n allowed: boolean;\n}\n\n// Libraries AuthZ types\nexport interface TeamMember {\n username: string;\n fullName: string;\n email: string;\n roles: string[];\n createdAt: string;\n scope: { resource: string; type: 'COURSE' | 'LIBRARY' | 'GLOBAL' };\n organization: string;\n role: string;\n}\n\nexport interface LibraryMetadata {\n id: string;\n org: string;\n title: string;\n slug: string;\n allowPublicRead: boolean;\n}\n\nexport interface RoleMetadata {\n role: string;\n name: string;\n description: string;\n contextType: string;\n disabled?: boolean;\n}\n// TODO: remove unnecessary fields when libraries gets removed\nexport interface Role extends RoleMetadata {\n scope: string;\n userCount: number;\n permissions: string[];\n}\n\nexport interface ResourceMetadata {\n key: string;\n label: string;\n description: string;\n icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>;\n}\n\nexport interface PermissionMetadata {\n key: string;\n resource: string;\n label?: string;\n description?: string;\n icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>;\n}\n\nexport type PermissionItem = ResourceMetadata & {\n perms: PermissionMetadata[];\n};\n\nexport interface Org {\n id: string;\n name: string;\n shortName: string;\n}\n\nexport interface Scope {\n externalKey: string;\n displayName: string;\n description?: string;\n org: Org | null;\n}\n\n// Permissions Matrix\n\nexport type EnrichedPermission = PermissionMetadata & {\n actionKey: string;\n};\n\nexport type PermissionWithRoles = EnrichedPermission & {\n roles: Record<string, boolean>;\n};\n\nexport type PermissionsResourceGrouped = ResourceMetadata & {\n permissions: PermissionWithRoles[];\n};\n\nexport interface UserRole {\n isSuperadmin?: boolean;\n role: string;\n org: string;\n scope: string;\n permissionCount: number;\n fullName?: string;\n username?: string;\n email?: string;\n}\n\nexport interface RoleToDelete {\n role: string;\n name?: string;\n scope: string;\n}\n\nexport type UserRoleWithPermissions = UserRole & {\n canManageScope?: boolean;\n};\n"]}
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"","sourcesContent":["export interface PermissionValidationRequest {\n action: string;\n scope?: string;\n}\n\nexport interface PermissionValidationResponse extends PermissionValidationRequest {\n allowed: boolean;\n}\n\nexport interface RoleMetadata {\n role: string;\n name: string;\n description: string;\n contextType: string;\n disabled?: boolean;\n}\n// TODO: remove unnecessary fields when libraries gets removed\nexport interface Role extends RoleMetadata {\n scope: string;\n userCount: number;\n permissions: string[];\n}\n\nexport interface ResourceMetadata {\n key: string;\n label: string;\n description: string;\n icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>;\n}\n\nexport interface PermissionMetadata {\n key: string;\n resource: string;\n label?: string;\n description?: string;\n icon?: React.ComponentType<React.SVGProps<SVGSVGElement>>;\n}\n\nexport type PermissionItem = ResourceMetadata & {\n perms: PermissionMetadata[];\n};\n\nexport interface Org {\n id: string;\n name: string;\n shortName: string;\n}\n\nexport interface Scope {\n externalKey: string;\n displayName: string;\n description?: string;\n org: Org | null;\n}\n\n// Permissions Matrix\n\nexport type EnrichedPermission = PermissionMetadata & {\n actionKey: string;\n};\n\nexport type PermissionWithRoles = EnrichedPermission & {\n roles: Record<string, boolean>;\n};\n\nexport type PermissionsResourceGrouped = ResourceMetadata & {\n permissions: PermissionWithRoles[];\n};\n\nexport interface UserRole {\n isSuperadmin?: boolean;\n role: string;\n org: string;\n scope: string;\n permissionCount: number;\n fullName?: string;\n username?: string;\n email?: string;\n}\n\nexport interface RoleToDelete {\n role: string;\n name?: string;\n scope: string;\n}\n\nexport type UserRoleWithPermissions = UserRole & {\n canManageScope?: boolean;\n};\n\n/**\n * A single role assignment nested under a team member in the user-grouped listing.\n * Mirrors `results[].assignments[]` of `GET /api/authz/v1/users/`.\n */\nexport interface TeamMemberAssignment {\n role: string;\n org: string;\n /** Scope external key (e.g. `course-v1:Org+Course+Run`). Identifies the scope and is\n * what the scope filter sends back to the API — not shown to the user. */\n scope: string;\n /** Human-readable scope name shown in the table. The API returns an empty string for\n * glob scopes and for scopes whose backing course or library no longer exists, so\n * callers fall back to `scope`. */\n scopeDisplayName: string;\n permissionCount: number;\n}\n\n/**\n * A team member as returned by the user-grouped assignments endpoint: one entry per\n * user, carrying the first few of their assignments. `assignmentCount` counts every\n * assignment behind that slice, so it can exceed `assignments.length`. Both are scoped\n * to the active role, org and scope filters and to what the caller may view.\n */\nexport interface TeamMember {\n username: string;\n fullName: string;\n email: string;\n assignmentCount: number;\n assignments: TeamMemberAssignment[];\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@openedx/frontend-app-admin-console",
3
- "version": "1.0.0-alpha.0",
3
+ "version": "1.0.0-alpha.10",
4
4
  "description": "Frontend Admin Console",
5
5
  "packageManager": "npm@11.12.1",
6
6
  "repository": {
@@ -59,7 +59,7 @@
59
59
  },
60
60
  "peerDependencies": {
61
61
  "@openedx/frontend-base": "^2.0.0-alpha.2 || 0.0.0-dev",
62
- "@openedx/paragon": "^23",
62
+ "@openedx/paragon": "^23 || 0.0.0-dev",
63
63
  "@tanstack/react-query": "^5",
64
64
  "react": "^18.3.1",
65
65
  "react-dom": "^18.3.1",
@@ -67,6 +67,8 @@
67
67
  },
68
68
  "devDependencies": {
69
69
  "@edx/browserslist-config": "^1.1.1",
70
+ "@openedx/frontend-base": "^2.0.0-alpha.2 || 0.0.0-dev",
71
+ "@openedx/paragon": "^23 || 0.0.0-dev",
70
72
  "@testing-library/dom": "^10.4.1",
71
73
  "@testing-library/jest-dom": "^6.6.4",
72
74
  "@testing-library/react": "^16.3.0",
@@ -77,7 +79,8 @@
77
79
  "@types/react-helmet": "^6.1.11",
78
80
  "nodemon": "^3.1.14",
79
81
  "tsc-alias": "^1.9.1",
80
- "turbo": "^2.10.11"
82
+ "turbo": "^2.10.11",
83
+ "typescript": "^5.9.3"
81
84
  },
82
85
  "dependencies": {
83
86
  "@edx/openedx-atlas": "^0.7.0",