@openedx/frontend-app-admin-console 1.0.0-alpha.6 → 1.0.0-alpha.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/authz-module/authz-home/index.js +20 -1
- package/dist/authz-module/authz-home/index.js.map +1 -1
- package/dist/authz-module/components/AddRoleButton.js +1 -1
- package/dist/authz-module/components/AddRoleButton.js.map +1 -1
- package/dist/authz-module/components/AuthZTitle.js +5 -3
- package/dist/authz-module/components/AuthZTitle.js.map +1 -1
- package/dist/authz-module/components/ExpandableButton.d.ts +17 -0
- package/dist/authz-module/components/ExpandableButton.js +16 -0
- package/dist/authz-module/components/ExpandableButton.js.map +1 -0
- package/dist/authz-module/components/TableCells.d.ts +5 -8
- package/dist/authz-module/components/TableCells.js +26 -59
- package/dist/authz-module/components/TableCells.js.map +1 -1
- package/dist/authz-module/components/TableControlBar/OrgFilter.js +2 -2
- package/dist/authz-module/components/TableControlBar/OrgFilter.js.map +1 -1
- package/dist/authz-module/components/TableControlBar/TableControlBar.d.ts +4 -1
- package/dist/authz-module/components/TableControlBar/TableControlBar.js +24 -24
- package/dist/authz-module/components/TableControlBar/TableControlBar.js.map +1 -1
- package/dist/authz-module/components/TableFooter/TableFooter.d.ts +14 -1
- package/dist/authz-module/components/TableFooter/TableFooter.js +3 -2
- package/dist/authz-module/components/TableFooter/TableFooter.js.map +1 -1
- package/dist/authz-module/components/constants.d.ts +1 -0
- package/dist/authz-module/components/constants.js +2 -1
- package/dist/authz-module/components/constants.js.map +1 -1
- package/dist/authz-module/components/messages.d.ts +0 -5
- package/dist/authz-module/components/messages.js +2 -7
- package/dist/authz-module/components/messages.js.map +1 -1
- package/dist/authz-module/constants.d.ts +14 -6
- package/dist/authz-module/constants.js +23 -6
- package/dist/authz-module/constants.js.map +1 -1
- package/dist/authz-module/data/api.d.ts +12 -4
- package/dist/authz-module/data/api.js +11 -2
- package/dist/authz-module/data/api.js.map +1 -1
- package/dist/authz-module/data/hooks.d.ts +8 -8
- package/dist/authz-module/data/hooks.js +15 -20
- package/dist/authz-module/data/hooks.js.map +1 -1
- package/dist/authz-module/hooks/useExclusiveRowExpansion.d.ts +7 -0
- package/dist/authz-module/hooks/useExclusiveRowExpansion.js +24 -0
- package/dist/authz-module/hooks/useExclusiveRowExpansion.js.map +1 -0
- package/dist/authz-module/index.scss +120 -2
- package/dist/authz-module/messages.d.ts +23 -0
- package/dist/authz-module/messages.js +18 -0
- package/dist/authz-module/messages.js.map +1 -1
- package/dist/authz-module/role-assignation-wizard/hooks/useScopeListData.js.map +1 -1
- package/dist/authz-module/team-members/TeamMembersTable.js +55 -17
- package/dist/authz-module/team-members/TeamMembersTable.js.map +1 -1
- package/dist/authz-module/team-members/components/AssignedRolesCell.d.ts +20 -0
- package/dist/authz-module/team-members/components/AssignedRolesCell.js +54 -0
- package/dist/authz-module/team-members/components/AssignedRolesCell.js.map +1 -0
- package/dist/authz-module/team-members/components/MoreRolesToggle.d.ts +14 -0
- package/dist/authz-module/team-members/components/MoreRolesToggle.js +25 -0
- package/dist/authz-module/team-members/components/MoreRolesToggle.js.map +1 -0
- package/dist/authz-module/team-members/components/TeamMemberCells.d.ts +17 -0
- package/dist/authz-module/team-members/components/TeamMemberCells.js +21 -0
- package/dist/authz-module/team-members/components/TeamMemberCells.js.map +1 -0
- package/dist/authz-module/team-members/components/TeamMemberViewActionCell.d.ts +18 -0
- package/dist/authz-module/team-members/components/TeamMemberViewActionCell.js +42 -0
- package/dist/authz-module/team-members/components/TeamMemberViewActionCell.js.map +1 -0
- package/dist/authz-module/team-members/components/UserAssignmentsSubTable.d.ts +16 -0
- package/dist/authz-module/team-members/components/UserAssignmentsSubTable.js +74 -0
- package/dist/authz-module/team-members/components/UserAssignmentsSubTable.js.map +1 -0
- package/dist/authz-module/team-members/index.d.ts +1 -0
- package/dist/authz-module/team-members/index.js +2 -0
- package/dist/authz-module/team-members/index.js.map +1 -0
- package/dist/authz-module/team-members/messages.d.ts +56 -1
- package/dist/authz-module/team-members/messages.js +59 -4
- package/dist/authz-module/team-members/messages.js.map +1 -1
- package/dist/authz-module/utils.d.ts +5 -0
- package/dist/authz-module/utils.js +10 -10
- package/dist/authz-module/utils.js.map +1 -1
- package/dist/components/LoadingPage.d.ts +3 -0
- package/dist/components/LoadingPage.js +9 -1
- package/dist/components/LoadingPage.js.map +1 -1
- package/dist/components/messages.d.ts +8 -0
- package/dist/components/messages.js +10 -0
- package/dist/components/messages.js.map +1 -0
- package/dist/types.d.ts +29 -20
- package/dist/types.js.map +1 -1
- package/package.json +1 -1
- package/dist/authz-module/components/ViewMoreLink.d.ts +0 -8
- package/dist/authz-module/components/ViewMoreLink.js +0 -8
- package/dist/authz-module/components/ViewMoreLink.js.map +0 -1
|
@@ -9,10 +9,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
|
|
|
9
9
|
};
|
|
10
10
|
import { useInfiniteQuery, useMutation, useQuery, useQueryClient, } from '@tanstack/react-query';
|
|
11
11
|
import { appId } from '../../constants';
|
|
12
|
-
import { assignTeamMembersRole,
|
|
12
|
+
import { assignTeamMembersRole, getTeamMembersAssignments, getOrgs, getScopes, revokeUserRoles, getUserAssignedRoles, validateUsers, getCourseAuthoringFlagStates, } from './api';
|
|
13
13
|
const authzQueryKeys = {
|
|
14
14
|
all: [appId, 'authz'],
|
|
15
|
-
|
|
15
|
+
teamMembersAssignments: (querySettings) => [...authzQueryKeys.all, 'teamMembersAssignments', querySettings],
|
|
16
16
|
orgs: (search, page, pageSize) => [...authzQueryKeys.all, 'organizations', search, page, pageSize],
|
|
17
17
|
scopes: (params) => [...authzQueryKeys.all, 'scopes', params],
|
|
18
18
|
userRoles: (username, querySettings) => [...authzQueryKeys.all, 'userRoles', username, querySettings],
|
|
@@ -34,7 +34,7 @@ export const useAssignTeamMembersRole = () => {
|
|
|
34
34
|
if (!error) {
|
|
35
35
|
queryClient.invalidateQueries({ queryKey: [...authzQueryKeys.all, 'userRoles'] });
|
|
36
36
|
queryClient.invalidateQueries({
|
|
37
|
-
predicate: (query) => query.queryKey.includes('
|
|
37
|
+
predicate: (query) => query.queryKey.includes('teamMembersAssignments'),
|
|
38
38
|
});
|
|
39
39
|
}
|
|
40
40
|
},
|
|
@@ -67,33 +67,28 @@ export const useRevokeUserRoles = () => {
|
|
|
67
67
|
predicate: (query) => query.queryKey.includes('userRoles'),
|
|
68
68
|
});
|
|
69
69
|
queryClient.invalidateQueries({
|
|
70
|
-
predicate: (query) => query.queryKey.includes('
|
|
70
|
+
predicate: (query) => query.queryKey.includes('teamMembersAssignments'),
|
|
71
71
|
});
|
|
72
72
|
},
|
|
73
73
|
});
|
|
74
74
|
};
|
|
75
75
|
/**
|
|
76
|
-
* React Query hook to fetch
|
|
77
|
-
*
|
|
78
|
-
*
|
|
79
|
-
*
|
|
76
|
+
* React Query hook to fetch team members grouped by user, with support for
|
|
77
|
+
* filtering, sorting, and pagination. Each result is one user carrying the first few of
|
|
78
|
+
* their role assignments plus how many there are; see `getTeamMembersAssignments` for
|
|
79
|
+
* how filters narrow both.
|
|
80
80
|
*
|
|
81
|
-
* @param querySettings -
|
|
81
|
+
* @param querySettings - Parameters for filtering by roles, scopes,
|
|
82
82
|
* organizations, search term, sorting, and pagination.
|
|
83
83
|
*
|
|
84
84
|
* @example
|
|
85
|
-
* const { data:
|
|
85
|
+
* const { data: teamMembers } = useTeamMembersAssignments({ roles: 'editor', pageSize: 20 });
|
|
86
86
|
*/
|
|
87
|
-
export const
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
retry: false,
|
|
93
|
-
refetchOnWindowFocus: false,
|
|
94
|
-
});
|
|
95
|
-
return result;
|
|
96
|
-
};
|
|
87
|
+
export const useTeamMembersAssignments = (querySettings) => (useQuery({
|
|
88
|
+
queryKey: authzQueryKeys.teamMembersAssignments(querySettings),
|
|
89
|
+
queryFn: () => getTeamMembersAssignments(querySettings),
|
|
90
|
+
staleTime: 1000 * 60 * 30, // refetch after 30 minutes
|
|
91
|
+
}));
|
|
97
92
|
/**
|
|
98
93
|
* React Query hook to fetch a paginated, searchable list of organizations.
|
|
99
94
|
* Results are cached for 30 minutes — suitable for both filter dropdowns and full listings.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/authz-module/data/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n useInfiniteQuery, useMutation, useQuery, useQueryClient,\n} from '@tanstack/react-query';\nimport { appId } from '@src/constants';\nimport {\n assignTeamMembersRole, AssignTeamMembersRoleRequest, getAllRoleAssignments,\n GetAllRoleAssignmentsResponse, getOrgs, GetOrgsResponse,\n getScopes, GetScopesResponse, QuerySettings, revokeUserRoles,\n RevokeUserRolesRequest, getUserAssignedRoles, GetUserAssignmentsResponse,\n validateUsers, ValidateUsersRequest, GetScopesParams,\n getCourseAuthoringFlagStates, CourseAuthoringFlagStates,\n} from './api';\n\nconst authzQueryKeys = {\n all: [appId, 'authz'] as const,\n allRoleAssignments: (querySettings?: QuerySettings) => [...authzQueryKeys.all, 'allRoleAssignments', querySettings] as const,\n orgs: (search?: string, page?: number, pageSize?: number) => [...authzQueryKeys.all, 'organizations', search, page, pageSize] as const,\n scopes: (params?: Omit<GetScopesParams, 'page'>) => [...authzQueryKeys.all, 'scopes', params] as const,\n userRoles: (username?: string, querySettings?: QuerySettings) => [...authzQueryKeys.all, 'userRoles', username, querySettings] as const,\n courseAuthoringFlagStates: () => [...authzQueryKeys.all, 'courseAuthoringFlagStates'] as const,\n};\n\n/**\n * React Query hook to add new team members to a specific scope or manage the corresponding roles.\n * It provides a mutation function to add users with specified roles to the team or assign new roles.\n *\n * @example\n * const { mutate: assignTeamMembersRole } = useAssignTeamMembersRole();\n * assignTeamMembersRole({ data: { libraryId: 'lib:123', users: ['jdoe'], role: 'editor' } });\n */\nexport const useAssignTeamMembersRole = () => {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: async ({ data }: {\n data: AssignTeamMembersRoleRequest;\n }) => assignTeamMembersRole(data),\n onSettled: (_data, error) => {\n if (!error) {\n queryClient.invalidateQueries({ queryKey: [...authzQueryKeys.all, 'userRoles'] });\n queryClient.invalidateQueries({\n predicate: (query) => query.queryKey.includes('allRoleAssignments'),\n });\n }\n },\n });\n};\n\n/**\n * React Query hook to validate users exist without assigning roles.\n * It checks if the provided usernames/email addresses are valid.\n *\n * @example\n * const { mutate: validateUsers } = useValidateUsers();\n * validateUsers({ data: { users: ['jdoe', 'jane@example.com'] } });\n */\nexport const useValidateUsers = () => useMutation({\n mutationFn: async ({ data }: {\n data: ValidateUsersRequest;\n }) => validateUsers(data),\n});\n\n/**\n * React Query hook to remove roles for a specific team member within a scope.\n *\n * @example\n * const { mutate: revokeUserRoles } = useRevokeUserRoles();\n * revokeUserRoles({ data: { libraryId: 'lib:123', users: ['jdoe'], role: 'editor' } });\n */\nexport const useRevokeUserRoles = () => {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: async ({ data }: {\n data: RevokeUserRolesRequest;\n }) => revokeUserRoles(data),\n onSettled: () => {\n queryClient.invalidateQueries({\n predicate: (query) => query.queryKey.includes('userRoles'),\n });\n queryClient.invalidateQueries({\n predicate: (query) => query.queryKey.includes('allRoleAssignments'),\n });\n },\n });\n};\n\n/**\n * React Query hook to fetch all role assignments across scopes and roles,\n * with support for filtering, sorting, and pagination.\n * It retrieves a comprehensive list of user-role assignments based\n * on the provided query settings.\n *\n * @param querySettings - Optional parameters for filtering by roles, scopes,\n * organizations, search term, sorting, and pagination.\n *\n * @example\n * const { data: roleAssignments } = useAllRoleAssignments({ roles: 'editor', pageSize: 20 });\n */\nexport const useAllRoleAssignments = (querySettings: QuerySettings) => {\n const result = useQuery<GetAllRoleAssignmentsResponse, Error>({\n queryKey: authzQueryKeys.allRoleAssignments(querySettings),\n queryFn: () => getAllRoleAssignments(querySettings),\n staleTime: 1000 * 60 * 30, // refetch after 30 minutes\n retry: false,\n refetchOnWindowFocus: false,\n });\n return result;\n};\n\n/**\n * React Query hook to fetch a paginated, searchable list of organizations.\n * Results are cached for 30 minutes — suitable for both filter dropdowns and full listings.\n *\n * @param search - Optional text filter applied to organization names.\n * @param page - 1-based page number; defaults to the first page when omitted.\n * @param pageSize - Items per page; defaults to the API default when omitted.\n * @returns A `QueryResult<GetOrgsResponse>` with `results`, `count`, `next`, and `previous`.\n *\n * @example\n * ```tsx\n * const { data } = useOrgs('edX');\n * const orgs = data?.results ?? [];\n * ```\n */\nexport const useOrgs = (search?: string, page?: number, pageSize?: number) => useQuery<GetOrgsResponse, Error>({\n queryKey: authzQueryKeys.orgs(search, page, pageSize),\n queryFn: () => getOrgs(search, page, pageSize),\n staleTime: 1000 * 60 * 30,\n refetchOnWindowFocus: false,\n});\n\n/**\n * React Query hook to fetch a paginated, filterable list of scopes (courses or libraries).\n * Uses infinite query to support infinite scroll — call `fetchNextPage` to load more results.\n *\n * @param params - Filter parameters (all optional):\n * - `search` — text filter applied to scope names\n * - `scopeType` — filter by scope type (e.g. course, library)\n * - `org` — filter by organization\n * - `pageSize` — number of items per page\n * - `managementPermissionOnly` — when true, returns only scopes the requester can manage\n * @returns An `InfiniteQueryResult` whose `data.pages` contains the accumulated `GetScopesResponse` pages.\n *\n * @example\n * ```tsx\n * const { data, fetchNextPage, hasNextPage } = useScopes({ search: 'intro', orgs: ['edX'] });\n * const scopes = data?.pages.flatMap((p) => p.results) ?? [];\n * ```\n */\nexport const useScopes = (params: Omit<GetScopesParams, 'page'> = {}) => useInfiniteQuery<GetScopesResponse, Error>({\n queryKey: authzQueryKeys.scopes(params),\n queryFn: ({ pageParam }) => getScopes({ ...params, page: pageParam as number }),\n getNextPageParam: (lastPage) => {\n if (!lastPage.next) return undefined;\n try {\n const nextUrl = new URL(lastPage.next);\n const page = nextUrl.searchParams.get('page');\n return page ? parseInt(page, 10) : undefined;\n } catch {\n return undefined;\n }\n },\n initialPageParam: 1,\n staleTime: 1000 * 60 * 5,\n refetchOnWindowFocus: false,\n});\n\n/*\n * React Query hook to fetch all the roles assigned to a specific user.\n * It retrieves the full list of roles with the corresponding permissions.\n * @param username - The username of the user\n * @param querySettings - Optional query parameters for filtering, sorting, and pagination\n *\n * @example\n * ```tsx\n * const { data: userRoles } = useUserAssignedRoles('jdoe', querySettings);\n * ```\n*/\nexport const useUserAssignedRoles = (\n username?: string,\n querySettings?: QuerySettings,\n) => useQuery<GetUserAssignmentsResponse, Error>({\n queryKey: authzQueryKeys.userRoles(username, querySettings),\n queryFn: () => getUserAssignedRoles(username, querySettings),\n staleTime: 1000 * 60 * 30, // refetch after 30 minutes\n enabled: !!username,\n refetchOnWindowFocus: false,\n});\n\n/**\n * React Query hook to fetch the enablement state of the course-authoring waffle flag\n * across scopes (global, org overrides, course overrides).\n *\n * Follows the same caching rules as the permission-validation hooks.\n *\n * @example\n * ```tsx\n * const { data } = useCourseAuthoringFlagStates();\n * ```\n */\nexport const useCourseAuthoringFlagStates = () => useQuery<CourseAuthoringFlagStates, Error>({\n queryKey: authzQueryKeys.courseAuthoringFlagStates(),\n queryFn: () => getCourseAuthoringFlagStates(),\n staleTime: 1000 * 60 * 30,\n refetchOnWindowFocus: false,\n retry: false,\n});\n"]}
|
|
1
|
+
{"version":3,"file":"hooks.js","sourceRoot":"","sources":["../../../src/authz-module/data/hooks.ts"],"names":[],"mappings":"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","sourcesContent":["import {\n useInfiniteQuery, useMutation, useQuery, useQueryClient,\n} from '@tanstack/react-query';\nimport { appId } from '@src/constants';\nimport {\n assignTeamMembersRole, AssignTeamMembersRoleRequest, getTeamMembersAssignments,\n GetTeamMembersAssignmentsResponse, getOrgs, GetOrgsResponse,\n getScopes, GetScopesResponse, QuerySettings, revokeUserRoles,\n RevokeUserRolesRequest, getUserAssignedRoles, GetUserAssignmentsResponse,\n validateUsers, ValidateUsersRequest, GetScopesParams,\n getCourseAuthoringFlagStates, CourseAuthoringFlagStates,\n} from './api';\n\nconst authzQueryKeys = {\n all: [appId, 'authz'] as const,\n teamMembersAssignments: (querySettings?: QuerySettings) => [...authzQueryKeys.all, 'teamMembersAssignments', querySettings] as const,\n orgs: (search?: string, page?: number, pageSize?: number) => [...authzQueryKeys.all, 'organizations', search, page, pageSize] as const,\n scopes: (params?: Omit<GetScopesParams, 'page'>) => [...authzQueryKeys.all, 'scopes', params] as const,\n userRoles: (username?: string, querySettings?: QuerySettings) => [...authzQueryKeys.all, 'userRoles', username, querySettings] as const,\n courseAuthoringFlagStates: () => [...authzQueryKeys.all, 'courseAuthoringFlagStates'] as const,\n};\n\n/**\n * React Query hook to add new team members to a specific scope or manage the corresponding roles.\n * It provides a mutation function to add users with specified roles to the team or assign new roles.\n *\n * @example\n * const { mutate: assignTeamMembersRole } = useAssignTeamMembersRole();\n * assignTeamMembersRole({ data: { libraryId: 'lib:123', users: ['jdoe'], role: 'editor' } });\n */\nexport const useAssignTeamMembersRole = () => {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: async ({ data }: {\n data: AssignTeamMembersRoleRequest;\n }) => assignTeamMembersRole(data),\n onSettled: (_data, error) => {\n if (!error) {\n queryClient.invalidateQueries({ queryKey: [...authzQueryKeys.all, 'userRoles'] });\n queryClient.invalidateQueries({\n predicate: (query) => query.queryKey.includes('teamMembersAssignments'),\n });\n }\n },\n });\n};\n\n/**\n * React Query hook to validate users exist without assigning roles.\n * It checks if the provided usernames/email addresses are valid.\n *\n * @example\n * const { mutate: validateUsers } = useValidateUsers();\n * validateUsers({ data: { users: ['jdoe', 'jane@example.com'] } });\n */\nexport const useValidateUsers = () => useMutation({\n mutationFn: async ({ data }: {\n data: ValidateUsersRequest;\n }) => validateUsers(data),\n});\n\n/**\n * React Query hook to remove roles for a specific team member within a scope.\n *\n * @example\n * const { mutate: revokeUserRoles } = useRevokeUserRoles();\n * revokeUserRoles({ data: { libraryId: 'lib:123', users: ['jdoe'], role: 'editor' } });\n */\nexport const useRevokeUserRoles = () => {\n const queryClient = useQueryClient();\n return useMutation({\n mutationFn: async ({ data }: {\n data: RevokeUserRolesRequest;\n }) => revokeUserRoles(data),\n onSettled: () => {\n queryClient.invalidateQueries({\n predicate: (query) => query.queryKey.includes('userRoles'),\n });\n queryClient.invalidateQueries({\n predicate: (query) => query.queryKey.includes('teamMembersAssignments'),\n });\n },\n });\n};\n\n/**\n * React Query hook to fetch team members grouped by user, with support for\n * filtering, sorting, and pagination. Each result is one user carrying the first few of\n * their role assignments plus how many there are; see `getTeamMembersAssignments` for\n * how filters narrow both.\n *\n * @param querySettings - Parameters for filtering by roles, scopes,\n * organizations, search term, sorting, and pagination.\n *\n * @example\n * const { data: teamMembers } = useTeamMembersAssignments({ roles: 'editor', pageSize: 20 });\n */\nexport const useTeamMembersAssignments = (querySettings: QuerySettings) => (\n useQuery<GetTeamMembersAssignmentsResponse, Error>({\n queryKey: authzQueryKeys.teamMembersAssignments(querySettings),\n queryFn: () => getTeamMembersAssignments(querySettings),\n staleTime: 1000 * 60 * 30, // refetch after 30 minutes\n })\n);\n\n/**\n * React Query hook to fetch a paginated, searchable list of organizations.\n * Results are cached for 30 minutes — suitable for both filter dropdowns and full listings.\n *\n * @param search - Optional text filter applied to organization names.\n * @param page - 1-based page number; defaults to the first page when omitted.\n * @param pageSize - Items per page; defaults to the API default when omitted.\n * @returns A `QueryResult<GetOrgsResponse>` with `results`, `count`, `next`, and `previous`.\n *\n * @example\n * ```tsx\n * const { data } = useOrgs('edX');\n * const orgs = data?.results ?? [];\n * ```\n */\nexport const useOrgs = (search?: string, page?: number, pageSize?: number) => useQuery<GetOrgsResponse, Error>({\n queryKey: authzQueryKeys.orgs(search, page, pageSize),\n queryFn: () => getOrgs(search, page, pageSize),\n staleTime: 1000 * 60 * 30,\n refetchOnWindowFocus: false,\n});\n\n/**\n * React Query hook to fetch a paginated, filterable list of scopes (courses or libraries).\n * Uses infinite query to support infinite scroll — call `fetchNextPage` to load more results.\n *\n * @param params - Filter parameters (all optional):\n * - `search` — text filter applied to scope names\n * - `scopeType` — filter by scope type (e.g. course, library)\n * - `org` — filter by organization\n * - `pageSize` — number of items per page\n * - `managementPermissionOnly` — when true, returns only scopes the requester can manage\n * @returns An `InfiniteQueryResult` whose `data.pages` contains the accumulated `GetScopesResponse` pages.\n *\n * @example\n * ```tsx\n * const { data, fetchNextPage, hasNextPage } = useScopes({ search: 'intro', orgs: ['edX'] });\n * const scopes = data?.pages.flatMap((p) => p.results) ?? [];\n * ```\n */\nexport const useScopes = (params: Omit<GetScopesParams, 'page'> = {}) => useInfiniteQuery<GetScopesResponse, Error>({\n queryKey: authzQueryKeys.scopes(params),\n queryFn: ({ pageParam }) => getScopes({ ...params, page: pageParam as number }),\n getNextPageParam: (lastPage) => {\n if (!lastPage.next) return undefined;\n try {\n const nextUrl = new URL(lastPage.next);\n const page = nextUrl.searchParams.get('page');\n return page ? parseInt(page, 10) : undefined;\n } catch {\n return undefined;\n }\n },\n initialPageParam: 1,\n staleTime: 1000 * 60 * 5,\n refetchOnWindowFocus: false,\n});\n\n/*\n * React Query hook to fetch all the roles assigned to a specific user.\n * It retrieves the full list of roles with the corresponding permissions.\n * @param username - The username of the user\n * @param querySettings - Optional query parameters for filtering, sorting, and pagination\n *\n * @example\n * ```tsx\n * const { data: userRoles } = useUserAssignedRoles('jdoe', querySettings);\n * ```\n*/\nexport const useUserAssignedRoles = (\n username?: string,\n querySettings?: QuerySettings,\n) => useQuery<GetUserAssignmentsResponse, Error>({\n queryKey: authzQueryKeys.userRoles(username, querySettings),\n queryFn: () => getUserAssignedRoles(username, querySettings),\n staleTime: 1000 * 60 * 30, // refetch after 30 minutes\n enabled: !!username,\n refetchOnWindowFocus: false,\n});\n\n/**\n * React Query hook to fetch the enablement state of the course-authoring waffle flag\n * across scopes (global, org overrides, course overrides).\n *\n * Follows the same caching rules as the permission-validation hooks.\n *\n * @example\n * ```tsx\n * const { data } = useCourseAuthoringFlagStates();\n * ```\n */\nexport const useCourseAuthoringFlagStates = () => useQuery<CourseAuthoringFlagStates, Error>({\n queryKey: authzQueryKeys.courseAuthoringFlagStates(),\n queryFn: () => getCourseAuthoringFlagStates(),\n staleTime: 1000 * 60 * 30,\n refetchOnWindowFocus: false,\n retry: false,\n});\n"]}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type DataTableRow } from '@openedx/paragon';
|
|
2
|
+
/**
|
|
3
|
+
* Returns a handler that toggles `row`, collapsing any other expanded row first so only one
|
|
4
|
+
* is open at a time. Paragon's `isExpandable` allows several, and a table whose rows expand
|
|
5
|
+
* into a nested table only reads clearly with one breakdown showing.
|
|
6
|
+
*/
|
|
7
|
+
export declare const useExclusiveRowExpansion: (row: DataTableRow<unknown>) => () => void;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { useContext } from 'react';
|
|
2
|
+
import { DataTableContext } from '@openedx/paragon';
|
|
3
|
+
/**
|
|
4
|
+
* Returns a handler that toggles `row`, collapsing any other expanded row first so only one
|
|
5
|
+
* is open at a time. Paragon's `isExpandable` allows several, and a table whose rows expand
|
|
6
|
+
* into a nested table only reads clearly with one breakdown showing.
|
|
7
|
+
*/
|
|
8
|
+
export const useExclusiveRowExpansion = (row) => {
|
|
9
|
+
const instance = useContext(DataTableContext);
|
|
10
|
+
return () => {
|
|
11
|
+
var _a, _b;
|
|
12
|
+
if (!row.isExpanded && instance) {
|
|
13
|
+
const expanded = ((_a = instance.state) === null || _a === void 0 ? void 0 : _a.expanded) || {};
|
|
14
|
+
Object.keys(expanded).forEach((rowId) => {
|
|
15
|
+
var _a;
|
|
16
|
+
if (rowId !== row.id && expanded[rowId]) {
|
|
17
|
+
(_a = instance.toggleRowExpanded) === null || _a === void 0 ? void 0 : _a.call(instance, rowId, false);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
}
|
|
21
|
+
(_b = row.toggleRowExpanded) === null || _b === void 0 ? void 0 : _b.call(row);
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
//# sourceMappingURL=useExclusiveRowExpansion.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"useExclusiveRowExpansion.js","sourceRoot":"","sources":["../../../src/authz-module/hooks/useExclusiveRowExpansion.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,OAAO,CAAC;AACnC,OAAO,EAAE,gBAAgB,EAAqB,MAAM,kBAAkB,CAAC;AASvE;;;;GAIG;AACH,MAAM,CAAC,MAAM,wBAAwB,GAAG,CAAC,GAA0B,EAAE,EAAE;IACrE,MAAM,QAAQ,GAAG,UAAU,CAAC,gBAAgB,CAAsB,CAAC;IAEnE,OAAO,GAAG,EAAE;;QACV,IAAI,CAAC,GAAG,CAAC,UAAU,IAAI,QAAQ,EAAE,CAAC;YAChC,MAAM,QAAQ,GAAG,CAAA,MAAA,QAAQ,CAAC,KAAK,0CAAE,QAAQ,KAAI,EAAE,CAAC;YAChD,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,OAAO,CAAC,CAAC,KAAK,EAAE,EAAE;;gBACtC,IAAI,KAAK,KAAK,GAAG,CAAC,EAAE,IAAI,QAAQ,CAAC,KAAK,CAAC,EAAE,CAAC;oBACxC,MAAA,QAAQ,CAAC,iBAAiB,yDAAG,KAAK,EAAE,KAAK,CAAC,CAAC;gBAC7C,CAAC;YACH,CAAC,CAAC,CAAC;QACL,CAAC;QACD,MAAA,GAAG,CAAC,iBAAiB,mDAAI,CAAC;IAC5B,CAAC,CAAC;AACJ,CAAC,CAAC","sourcesContent":["import { useContext } from 'react';\nimport { DataTableContext, type DataTableRow } from '@openedx/paragon';\n\ninterface DataTableInstance {\n state?: {\n expanded?: Record<string, boolean>;\n };\n toggleRowExpanded?: (rowId: string, expanded: boolean) => void;\n}\n\n/**\n * Returns a handler that toggles `row`, collapsing any other expanded row first so only one\n * is open at a time. Paragon's `isExpandable` allows several, and a table whose rows expand\n * into a nested table only reads clearly with one breakdown showing.\n */\nexport const useExclusiveRowExpansion = (row: DataTableRow<unknown>) => {\n const instance = useContext(DataTableContext) as DataTableInstance;\n\n return () => {\n if (!row.isExpanded && instance) {\n const expanded = instance.state?.expanded || {};\n Object.keys(expanded).forEach((rowId) => {\n if (rowId !== row.id && expanded[rowId]) {\n instance.toggleRowExpanded?.(rowId, false);\n }\n });\n }\n row.toggleRowExpanded?.();\n };\n};\n"]}
|
|
@@ -26,7 +26,6 @@
|
|
|
26
26
|
|
|
27
27
|
.authz-action-divider {
|
|
28
28
|
border-right: var(--pgn-size-border-width) solid var(--pgn-color-border);
|
|
29
|
-
height: var(--height-action-divider);
|
|
30
29
|
width: 0;
|
|
31
30
|
}
|
|
32
31
|
|
|
@@ -73,6 +72,120 @@
|
|
|
73
72
|
max-height: 80vh;
|
|
74
73
|
}
|
|
75
74
|
}
|
|
75
|
+
|
|
76
|
+
// Where `.text-truncate` cuts; sized to the design's character budget.
|
|
77
|
+
.authz-cell-username { max-width: 15rem; } // ~24 characters
|
|
78
|
+
.authz-cell-email { max-width: 17rem; } // ~28 characters
|
|
79
|
+
|
|
80
|
+
&.team-members-table {
|
|
81
|
+
// Every row is two lines tall: the assigned-roles cell stacks scope over org.
|
|
82
|
+
th,
|
|
83
|
+
td {
|
|
84
|
+
padding-top: var(--pgn-spacing-spacer-3);
|
|
85
|
+
padding-bottom: var(--pgn-spacing-spacer-3);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
// Keep the "+N more roles" label on one line.
|
|
89
|
+
td:nth-child(4) {
|
|
90
|
+
white-space: nowrap;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
/*
|
|
94
|
+
Auto-layout starves "Assigned roles" in favour of the short Username/Email values,
|
|
95
|
+
so widths are pinned. Below `md` these percentages would crush the toggle and action
|
|
96
|
+
columns, so the table falls back to content sizing and scrolls inside Paragon's
|
|
97
|
+
`.pgn__data-table-container`.
|
|
98
|
+
*/
|
|
99
|
+
@media (--pgn-size-breakpoint-min-width-md) {
|
|
100
|
+
.pgn__data-table {
|
|
101
|
+
table-layout: fixed;
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
th:nth-child(1) { width: 16%; } // Username
|
|
105
|
+
th:nth-child(2) { width: 18%; } // Email
|
|
106
|
+
th:nth-child(3) { width: 46%; } // Assigned roles
|
|
107
|
+
th:nth-child(4) { width: 13%; } // "+N more roles" toggle
|
|
108
|
+
th:nth-child(5) { width: 7%; } // Actions
|
|
109
|
+
|
|
110
|
+
td {
|
|
111
|
+
overflow: hidden;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
// The nested table sizes its own columns; it must not inherit the widths above.
|
|
115
|
+
.team-members-table__subtable {
|
|
116
|
+
.pgn__data-table {
|
|
117
|
+
table-layout: auto;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
th:nth-child(1),
|
|
121
|
+
th:nth-child(2),
|
|
122
|
+
th:nth-child(3) {
|
|
123
|
+
width: auto;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// One line per row, so it keeps tighter padding.
|
|
129
|
+
.team-members-table__subtable {
|
|
130
|
+
th,
|
|
131
|
+
td {
|
|
132
|
+
padding-top: var(--pgn-spacing-spacer-2);
|
|
133
|
+
padding-bottom: var(--pgn-spacing-spacer-2);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
.team-members-table__subtable {
|
|
139
|
+
// The module turns `hr` horizontal below `sm`; this footer divider stays vertical.
|
|
140
|
+
hr {
|
|
141
|
+
border-right: var(--pgn-size-border-width) solid var(--pgn-color-border);
|
|
142
|
+
border-top: none;
|
|
143
|
+
height: 1rem;
|
|
144
|
+
width: 0;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
// The Card already pads; the module's blanket 1rem would double it up.
|
|
148
|
+
.pgn__data-table-wrapper {
|
|
149
|
+
padding: 0;
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
// Matches PermissionTable's `bg-info-100 text-primary` band. Paragon renders its own
|
|
153
|
+
// `thead`, so the tokens are used rather than the utility classes.
|
|
154
|
+
thead th {
|
|
155
|
+
background-color: var(--pgn-color-info-100);
|
|
156
|
+
color: var(--pgn-color-primary-base);
|
|
157
|
+
}
|
|
158
|
+
|
|
159
|
+
/*
|
|
160
|
+
Paragon stripes with `.pgn__data-table.is-striped tr:nth-child(2n)` — a descendant
|
|
161
|
+
selector, so the striped outer table reaches in and `isStriped={false}` on the inner
|
|
162
|
+
one cannot override it. The `:not()` keeps a nested table that wants stripes.
|
|
163
|
+
*/
|
|
164
|
+
.pgn__data-table:not(.is-striped) tr {
|
|
165
|
+
background-color: transparent;
|
|
166
|
+
|
|
167
|
+
// With the stripes gone, rows need an explicit divider to stay readable.
|
|
168
|
+
&:not(:last-child) td {
|
|
169
|
+
border-bottom: var(--pgn-size-border-width) solid var(--pgn-color-border);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
// Floor the badge to the widest label, so "in" and the scope align down the column.
|
|
175
|
+
.authz-assigned-roles .authz-role-badge { min-width: 11rem; }
|
|
176
|
+
|
|
177
|
+
.authz-scope-cell {
|
|
178
|
+
min-width: 0; // Lets the flex child shrink far enough for `.text-truncate` to cut.
|
|
179
|
+
max-width: 25rem; // org subline, ~40 characters
|
|
180
|
+
|
|
181
|
+
&__name {
|
|
182
|
+
max-width: 22rem; // ~35 characters
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
&__org {
|
|
186
|
+
line-height: 1.2;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
76
189
|
|
|
77
190
|
@media(--pgn-size-breakpoint-max-width-sm){
|
|
78
191
|
.flex-column-sm {
|
|
@@ -84,6 +197,12 @@
|
|
|
84
197
|
width: 100%;
|
|
85
198
|
}
|
|
86
199
|
}
|
|
200
|
+
|
|
201
|
+
@media (--pgn-size-breakpoint-min-width-md) {
|
|
202
|
+
.authz-action-divider {
|
|
203
|
+
height: var(--height-action-divider);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
87
206
|
}
|
|
88
207
|
|
|
89
208
|
.highlighted-users-input {
|
|
@@ -132,4 +251,3 @@
|
|
|
132
251
|
.scope-list {
|
|
133
252
|
max-height: 500px;
|
|
134
253
|
}
|
|
135
|
-
|
|
@@ -39,10 +39,33 @@ declare const messages: {
|
|
|
39
39
|
defaultMessage: string;
|
|
40
40
|
description: string;
|
|
41
41
|
};
|
|
42
|
+
'authz.scope.aggregate.course': {
|
|
43
|
+
id: string;
|
|
44
|
+
defaultMessage: string;
|
|
45
|
+
description: string;
|
|
46
|
+
};
|
|
47
|
+
'authz.scope.aggregate.library': {
|
|
48
|
+
id: string;
|
|
49
|
+
defaultMessage: string;
|
|
50
|
+
description: string;
|
|
51
|
+
};
|
|
42
52
|
'authz.team.toast.retry.label': {
|
|
43
53
|
id: string;
|
|
44
54
|
defaultMessage: string;
|
|
45
55
|
description: string;
|
|
46
56
|
};
|
|
47
57
|
};
|
|
58
|
+
/** Aggregate scope labels keyed by the kind of resource the scope covers. */
|
|
59
|
+
export declare const AGGREGATE_SCOPE_LABELS: {
|
|
60
|
+
course: {
|
|
61
|
+
id: string;
|
|
62
|
+
defaultMessage: string;
|
|
63
|
+
description: string;
|
|
64
|
+
};
|
|
65
|
+
library: {
|
|
66
|
+
id: string;
|
|
67
|
+
defaultMessage: string;
|
|
68
|
+
description: string;
|
|
69
|
+
};
|
|
70
|
+
};
|
|
48
71
|
export default messages;
|
|
@@ -40,11 +40,29 @@ const messages = defineMessages({
|
|
|
40
40
|
defaultMessage: '<Bold>The request took too long.</Bold> <Br></Br>Please check your connection and try again.',
|
|
41
41
|
description: 'Request timeout message',
|
|
42
42
|
},
|
|
43
|
+
// Aggregate scope labels, shared by every table that lists a scope beside its
|
|
44
|
+
// organization. The scope column names only the kind of resource; the organization
|
|
45
|
+
// column says how far it reaches. The wizard keeps its own, longer wording.
|
|
46
|
+
'authz.scope.aggregate.course': {
|
|
47
|
+
id: 'authz.scope.aggregate.course',
|
|
48
|
+
defaultMessage: 'All courses',
|
|
49
|
+
description: 'Scope column label for a role that covers every course, either across the platform or within one organization. The organization column shows which.',
|
|
50
|
+
},
|
|
51
|
+
'authz.scope.aggregate.library': {
|
|
52
|
+
id: 'authz.scope.aggregate.library',
|
|
53
|
+
defaultMessage: 'All libraries',
|
|
54
|
+
description: 'Scope column label for a role that covers every library, either across the platform or within one organization. The organization column shows which.',
|
|
55
|
+
},
|
|
43
56
|
'authz.team.toast.retry.label': {
|
|
44
57
|
id: 'authz.team.toast.retry.label',
|
|
45
58
|
defaultMessage: 'Retry',
|
|
46
59
|
description: 'Label for retry button.',
|
|
47
60
|
},
|
|
48
61
|
});
|
|
62
|
+
/** Aggregate scope labels keyed by the kind of resource the scope covers. */
|
|
63
|
+
export const AGGREGATE_SCOPE_LABELS = {
|
|
64
|
+
course: messages['authz.scope.aggregate.course'],
|
|
65
|
+
library: messages['authz.scope.aggregate.library'],
|
|
66
|
+
};
|
|
49
67
|
export default messages;
|
|
50
68
|
//# sourceMappingURL=messages.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/authz-module/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAC7B;IACE,gCAAgC,EAAE;QAChC,EAAE,EAAE,gCAAgC;QACpC,cAAc,EAAE,kCAAkC;QAClD,WAAW,EAAE,+EAA+E;KAC7F;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,aAAa;QAC7B,WAAW,EAAE,iCAAiC;KAC/C;IACD,wCAAwC,EAAE;QACxC,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,gFAAgF;QAChG,WAAW,EAAE,uBAAuB;KACrC;IACD,kDAAkD,EAAE;QAClD,EAAE,EAAE,kDAAkD;QACtD,cAAc,EAAE,6KAA6K;QAC7L,WAAW,EAAE,2CAA2C;KACzD;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,2FAA2F;QAC3G,WAAW,EAAE,+BAA+B;KAC7C;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,iGAAiG;QACjH,WAAW,EAAE,2BAA2B;KACzC;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,kGAAkG;QAClH,WAAW,EAAE,yCAAyC;KACvD;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,8FAA8F;QAC9G,WAAW,EAAE,yBAAyB;KACvC;IACD,8BAA8B,EAAE;QAC9B,EAAE,EAAE,8BAA8B;QAClC,cAAc,EAAE,OAAO;QACvB,WAAW,EAAE,yBAAyB;KACvC;CACF,CACF,CAAC;AAEF,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages(\n {\n 'authz.management.home.nav.link': {\n id: 'authz.management.home.nav.link',\n defaultMessage: 'Roles and Permissions Management',\n description: 'Text for the roles and permissions management home page title navigation link',\n },\n 'authz.management.assign.role.title': {\n id: 'authz.management.assign.role.title',\n defaultMessage: 'Assign Role',\n description: 'Text for the assign role button',\n },\n 'authz.team.toast.default.error.message': {\n id: 'authz.team.toast.default.error.message',\n defaultMessage: '<Bold>Something went wrong on our end.</Bold> <Br></Br>Please try again later.',\n description: 'Default error message',\n },\n 'authz.team.remove.user.toast.success.description': {\n id: 'authz.team.remove.user.toast.success.description',\n defaultMessage: 'The {role} role has been successfully removed.{rolesCount, plural, =0 { The user no longer has access to this library and has been removed from the member list.} other {}}',\n description: 'Team management remove user toast success',\n },\n 'authz.team.toast.500.error.message': {\n id: 'authz.team.toast.500.error.message',\n defaultMessage: '<Bold>We\\'re experiencing technical difficulties.</Bold> <Br></Br>Please try again later.',\n description: 'Internal server error message',\n },\n 'authz.team.toast.502.error.message': {\n id: 'authz.team.toast.502.error.message',\n defaultMessage: '<Bold>We\\'re having trouble connecting to our services.</Bold> <Br></Br>Please try again later.',\n description: 'Bad gateway error message',\n },\n 'authz.team.toast.503.error.message': {\n id: 'authz.team.toast.503.error.message',\n defaultMessage: '<Bold>The service is temporarily unavailable.</Bold> <Br></Br>Please try again in a few moments.',\n description: 'Service temporarily unavailable message',\n },\n 'authz.team.toast.408.error.message': {\n id: 'authz.team.toast.408.error.message',\n defaultMessage: '<Bold>The request took too long.</Bold> <Br></Br>Please check your connection and try again.',\n description: 'Request timeout message',\n },\n 'authz.team.toast.retry.label': {\n id: 'authz.team.toast.retry.label',\n defaultMessage: 'Retry',\n description: 'Label for retry button.',\n },\n },\n);\n\nexport default messages;\n"]}
|
|
1
|
+
{"version":3,"file":"messages.js","sourceRoot":"","sources":["../../src/authz-module/messages.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,wBAAwB,CAAC;AAExD,MAAM,QAAQ,GAAG,cAAc,CAC7B;IACE,gCAAgC,EAAE;QAChC,EAAE,EAAE,gCAAgC;QACpC,cAAc,EAAE,kCAAkC;QAClD,WAAW,EAAE,+EAA+E;KAC7F;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,aAAa;QAC7B,WAAW,EAAE,iCAAiC;KAC/C;IACD,wCAAwC,EAAE;QACxC,EAAE,EAAE,wCAAwC;QAC5C,cAAc,EAAE,gFAAgF;QAChG,WAAW,EAAE,uBAAuB;KACrC;IACD,kDAAkD,EAAE;QAClD,EAAE,EAAE,kDAAkD;QACtD,cAAc,EAAE,6KAA6K;QAC7L,WAAW,EAAE,2CAA2C;KACzD;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,2FAA2F;QAC3G,WAAW,EAAE,+BAA+B;KAC7C;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,iGAAiG;QACjH,WAAW,EAAE,2BAA2B;KACzC;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,kGAAkG;QAClH,WAAW,EAAE,yCAAyC;KACvD;IACD,oCAAoC,EAAE;QACpC,EAAE,EAAE,oCAAoC;QACxC,cAAc,EAAE,8FAA8F;QAC9G,WAAW,EAAE,yBAAyB;KACvC;IACD,8EAA8E;IAC9E,mFAAmF;IACnF,4EAA4E;IAC5E,8BAA8B,EAAE;QAC9B,EAAE,EAAE,8BAA8B;QAClC,cAAc,EAAE,aAAa;QAC7B,WAAW,EAAE,qJAAqJ;KACnK;IACD,+BAA+B,EAAE;QAC/B,EAAE,EAAE,+BAA+B;QACnC,cAAc,EAAE,eAAe;QAC/B,WAAW,EAAE,sJAAsJ;KACpK;IACD,8BAA8B,EAAE;QAC9B,EAAE,EAAE,8BAA8B;QAClC,cAAc,EAAE,OAAO;QACvB,WAAW,EAAE,yBAAyB;KACvC;CACF,CACF,CAAC;AAEF,6EAA6E;AAC7E,MAAM,CAAC,MAAM,sBAAsB,GAAG;IACpC,MAAM,EAAE,QAAQ,CAAC,8BAA8B,CAAC;IAChD,OAAO,EAAE,QAAQ,CAAC,+BAA+B,CAAC;CACnD,CAAC;AAEF,eAAe,QAAQ,CAAC","sourcesContent":["import { defineMessages } from '@openedx/frontend-base';\n\nconst messages = defineMessages(\n {\n 'authz.management.home.nav.link': {\n id: 'authz.management.home.nav.link',\n defaultMessage: 'Roles and Permissions Management',\n description: 'Text for the roles and permissions management home page title navigation link',\n },\n 'authz.management.assign.role.title': {\n id: 'authz.management.assign.role.title',\n defaultMessage: 'Assign Role',\n description: 'Text for the assign role button',\n },\n 'authz.team.toast.default.error.message': {\n id: 'authz.team.toast.default.error.message',\n defaultMessage: '<Bold>Something went wrong on our end.</Bold> <Br></Br>Please try again later.',\n description: 'Default error message',\n },\n 'authz.team.remove.user.toast.success.description': {\n id: 'authz.team.remove.user.toast.success.description',\n defaultMessage: 'The {role} role has been successfully removed.{rolesCount, plural, =0 { The user no longer has access to this library and has been removed from the member list.} other {}}',\n description: 'Team management remove user toast success',\n },\n 'authz.team.toast.500.error.message': {\n id: 'authz.team.toast.500.error.message',\n defaultMessage: '<Bold>We\\'re experiencing technical difficulties.</Bold> <Br></Br>Please try again later.',\n description: 'Internal server error message',\n },\n 'authz.team.toast.502.error.message': {\n id: 'authz.team.toast.502.error.message',\n defaultMessage: '<Bold>We\\'re having trouble connecting to our services.</Bold> <Br></Br>Please try again later.',\n description: 'Bad gateway error message',\n },\n 'authz.team.toast.503.error.message': {\n id: 'authz.team.toast.503.error.message',\n defaultMessage: '<Bold>The service is temporarily unavailable.</Bold> <Br></Br>Please try again in a few moments.',\n description: 'Service temporarily unavailable message',\n },\n 'authz.team.toast.408.error.message': {\n id: 'authz.team.toast.408.error.message',\n defaultMessage: '<Bold>The request took too long.</Bold> <Br></Br>Please check your connection and try again.',\n description: 'Request timeout message',\n },\n // Aggregate scope labels, shared by every table that lists a scope beside its\n // organization. The scope column names only the kind of resource; the organization\n // column says how far it reaches. The wizard keeps its own, longer wording.\n 'authz.scope.aggregate.course': {\n id: 'authz.scope.aggregate.course',\n defaultMessage: 'All courses',\n description: 'Scope column label for a role that covers every course, either across the platform or within one organization. The organization column shows which.',\n },\n 'authz.scope.aggregate.library': {\n id: 'authz.scope.aggregate.library',\n defaultMessage: 'All libraries',\n description: 'Scope column label for a role that covers every library, either across the platform or within one organization. The organization column shows which.',\n },\n 'authz.team.toast.retry.label': {\n id: 'authz.team.toast.retry.label',\n defaultMessage: 'Retry',\n description: 'Label for retry button.',\n },\n },\n);\n\n/** Aggregate scope labels keyed by the kind of resource the scope covers. */\nexport const AGGREGATE_SCOPE_LABELS = {\n course: messages['authz.scope.aggregate.course'],\n library: messages['authz.scope.aggregate.library'],\n};\n\nexport default messages;\n"]}
|
|
@@ -1 +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"]}
|
|
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 '../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"]}
|
|
@@ -6,18 +6,36 @@ import { DataTable, TextFilter, } from '@openedx/paragon';
|
|
|
6
6
|
import { useToastManager } from '../../components/ToastManager/ToastManagerContext';
|
|
7
7
|
import { LIBRARY_ROLE_KEYS } from '../roles-permissions';
|
|
8
8
|
import { useViewTeamPermissions } from '../hooks/useViewTeamPermissions';
|
|
9
|
-
import { useCourseAuthoringFlag } from '../hooks/useCourseAuthoringFlag';
|
|
10
9
|
import { useQuerySettings } from '../hooks/useQuerySettings';
|
|
11
10
|
import OrgFilter from '../components/TableControlBar/OrgFilter';
|
|
12
11
|
import RolesFilter from '../components/TableControlBar/RolesFilter';
|
|
13
12
|
import ScopesFilter from '../components/TableControlBar/ScopesFilter';
|
|
14
13
|
import TableControlBar from '../components/TableControlBar/TableControlBar';
|
|
15
14
|
import { getCellHeader } from '../utils';
|
|
16
|
-
import {
|
|
17
|
-
import { useAllRoleAssignments } from '../data/hooks';
|
|
15
|
+
import { useTeamMembersAssignments } from '../data/hooks';
|
|
18
16
|
import { TABLE_DEFAULT_PAGE_SIZE } from '../constants';
|
|
19
17
|
import messages from './messages';
|
|
20
18
|
import TableFooter from '../components/TableFooter/TableFooter';
|
|
19
|
+
import AssignedRolesCell from './components/AssignedRolesCell';
|
|
20
|
+
import { EmailCell, NameCell } from './components/TeamMemberCells';
|
|
21
|
+
import MoreRolesToggle from './components/MoreRolesToggle';
|
|
22
|
+
import UserAssignmentsSubTable from './components/UserAssignmentsSubTable';
|
|
23
|
+
import TeamMemberViewActionCell from './components/TeamMemberViewActionCell';
|
|
24
|
+
const toastedErrors = new WeakSet();
|
|
25
|
+
/**
|
|
26
|
+
* Stable stand-in for a query that has not resolved. A fresh object literal here would
|
|
27
|
+
* hand `DataTable` a new `data` array on every render, and react-table resets its
|
|
28
|
+
* expanded-row state whenever `data` changes identity.
|
|
29
|
+
*/
|
|
30
|
+
const EMPTY_ASSIGNMENTS = {
|
|
31
|
+
results: [], count: 0, next: null, previous: null,
|
|
32
|
+
};
|
|
33
|
+
// Org, scope and role stay in the column set — TableControlBar derives its filter
|
|
34
|
+
// controls from the columns — but are hidden, since the design surfaces them inside
|
|
35
|
+
// each user's role breakdown instead of as top-level columns. They carry no accessor
|
|
36
|
+
// (a user row has no single org/scope/role), so each opts into filtering with
|
|
37
|
+
// `defaultCanFilter`, which react-table otherwise infers from the accessor.
|
|
38
|
+
const HIDDEN_FILTER_COLUMNS = ['org', 'scope', 'role'];
|
|
21
39
|
const TeamMembersTable = ({ presetScope }) => {
|
|
22
40
|
const intl = useIntl();
|
|
23
41
|
const { showErrorToast } = useToastManager();
|
|
@@ -34,33 +52,45 @@ const TeamMembersTable = ({ presetScope }) => {
|
|
|
34
52
|
} : undefined;
|
|
35
53
|
const { querySettings, handleTableFetch } = useQuerySettings(initialQuerySettings);
|
|
36
54
|
const { isCourseViewAllowed } = useViewTeamPermissions();
|
|
37
|
-
const { isCourseEnabled } = useCourseAuthoringFlag();
|
|
38
55
|
const effectiveQuerySettings = useMemo(() => {
|
|
39
56
|
if (isCourseViewAllowed || querySettings.roles)
|
|
40
57
|
return querySettings;
|
|
41
58
|
return Object.assign(Object.assign({}, querySettings), { roles: LIBRARY_ROLE_KEYS });
|
|
42
59
|
}, [isCourseViewAllowed, querySettings]);
|
|
43
|
-
const { data: { results:
|
|
44
|
-
const viewActionCell = useMemo(() => createViewActionCell({ isCourseEnabled }), [isCourseEnabled]);
|
|
60
|
+
const { data: { results: teamMembers, count } = EMPTY_ASSIGNMENTS, isLoading: isLoadingTeamMembers, error, refetch, } = useTeamMembersAssignments(effectiveQuerySettings);
|
|
45
61
|
const initialFilters = presetScope ? [{ id: 'scope', value: [presetScope] }] : [];
|
|
62
|
+
/**
|
|
63
|
+
* Only transient failures reach here.
|
|
64
|
+
*/
|
|
46
65
|
useEffect(() => {
|
|
47
|
-
if (error) {
|
|
66
|
+
if (error && !toastedErrors.has(error)) {
|
|
67
|
+
toastedErrors.add(error);
|
|
48
68
|
showErrorToast(error, refetch);
|
|
49
69
|
}
|
|
50
70
|
}, [error, showErrorToast, refetch]);
|
|
51
71
|
const pageCount = Math.ceil(count / TABLE_DEFAULT_PAGE_SIZE);
|
|
52
72
|
const fetchData = useMemo(() => debounce(handleTableFetch, 500), [handleTableFetch]);
|
|
53
73
|
useEffect(() => () => fetchData.cancel(), [fetchData]);
|
|
54
|
-
|
|
74
|
+
const showingUsersLabel = intl.formatMessage(messages['authz.team.members.table.showing.users.text'], { pageSize: teamMembers.length, itemCount: count });
|
|
75
|
+
return (_jsxs(DataTable, { isExpandable: true, isFilterable: true, isPaginated: true, isSortable: true, manualFilters: true, manualPagination: true, manualSortBy: true, numBreakoutFilters: 4, fetchData: fetchData, data: teamMembers, itemCount: count, pageCount: pageCount, initialState: {
|
|
76
|
+
pageSize: TABLE_DEFAULT_PAGE_SIZE,
|
|
77
|
+
filters: initialFilters,
|
|
78
|
+
hiddenColumns: HIDDEN_FILTER_COLUMNS,
|
|
79
|
+
}, isLoading: isLoadingTeamMembers, renderRowSubComponent: ({ row }) => (_jsx(UserAssignmentsSubTable, { row: row })), additionalColumns: [
|
|
80
|
+
{
|
|
81
|
+
id: 'moreRoles',
|
|
82
|
+
Header: '',
|
|
83
|
+
Cell: MoreRolesToggle,
|
|
84
|
+
},
|
|
55
85
|
{
|
|
56
86
|
id: 'action',
|
|
57
87
|
Header: intl.formatMessage(messages['authz.team.members.table.column.actions.title']),
|
|
58
|
-
Cell:
|
|
88
|
+
Cell: TeamMemberViewActionCell,
|
|
59
89
|
},
|
|
60
90
|
], columns: [
|
|
61
91
|
{
|
|
62
92
|
id: 'username',
|
|
63
|
-
Header: intl.formatMessage(messages['authz.team.members.table.column.
|
|
93
|
+
Header: intl.formatMessage(messages['authz.team.members.table.column.username.title']),
|
|
64
94
|
accessor: 'username',
|
|
65
95
|
Cell: NameCell,
|
|
66
96
|
filter: 'text',
|
|
@@ -70,38 +100,46 @@ const TeamMembersTable = ({ presetScope }) => {
|
|
|
70
100
|
{
|
|
71
101
|
Header: intl.formatMessage(messages['authz.team.members.table.column.email.title']),
|
|
72
102
|
accessor: 'email',
|
|
103
|
+
Cell: EmailCell,
|
|
73
104
|
disableFilters: true,
|
|
74
105
|
filter: 'text',
|
|
75
106
|
Filter: TextFilter,
|
|
76
107
|
},
|
|
77
108
|
{
|
|
109
|
+
id: 'assignedRoles',
|
|
110
|
+
Header: intl.formatMessage(messages['authz.team.members.table.column.assigned.roles.title']),
|
|
111
|
+
Cell: AssignedRolesCell,
|
|
112
|
+
disableFilters: true,
|
|
113
|
+
disableSortBy: true,
|
|
114
|
+
},
|
|
115
|
+
{
|
|
116
|
+
id: 'org',
|
|
78
117
|
Header: getCellHeader('org', intl.formatMessage(messages['authz.team.members.table.column.organization.title']), columnsWithFiltersApplied),
|
|
79
|
-
accessor: 'org',
|
|
80
|
-
Cell: OrgCell,
|
|
81
118
|
filter: 'includesValue',
|
|
119
|
+
defaultCanFilter: true,
|
|
82
120
|
Filter: OrgFilter,
|
|
83
121
|
filterButtonText: intl.formatMessage(messages['authz.team.members.table.column.organization.title']),
|
|
84
122
|
filterOrder: 2,
|
|
85
123
|
},
|
|
86
124
|
{
|
|
125
|
+
id: 'scope',
|
|
87
126
|
Header: getCellHeader('scope', intl.formatMessage(messages['authz.team.members.table.column.scope.title']), columnsWithFiltersApplied),
|
|
88
|
-
accessor: 'scope',
|
|
89
|
-
Cell: ScopeCell,
|
|
90
127
|
filter: 'includesValue',
|
|
128
|
+
defaultCanFilter: true,
|
|
91
129
|
Filter: ScopesFilter,
|
|
92
130
|
filterButtonText: intl.formatMessage(messages['authz.team.members.table.column.scope.title']),
|
|
93
131
|
filterOrder: 4,
|
|
94
132
|
},
|
|
95
133
|
{
|
|
134
|
+
id: 'role',
|
|
96
135
|
Header: getCellHeader('role', intl.formatMessage(messages['authz.team.members.table.column.role.title']), columnsWithFiltersApplied),
|
|
97
|
-
accessor: 'role',
|
|
98
136
|
filter: 'includesValue',
|
|
99
|
-
|
|
137
|
+
defaultCanFilter: true,
|
|
100
138
|
Filter: RolesFilter,
|
|
101
139
|
filterButtonText: intl.formatMessage(messages['authz.team.members.table.column.role.title']),
|
|
102
140
|
filterOrder: 3,
|
|
103
141
|
},
|
|
104
|
-
], children: [_jsx(TableControlBar, { onFilterChange: setColumnsWithFiltersApplied }), _jsx(DataTable.Table, {}), _jsx(TableFooter, {})] }));
|
|
142
|
+
], children: [_jsx(TableControlBar, { onFilterChange: setColumnsWithFiltersApplied, countLabel: showingUsersLabel }), _jsx(DataTable.Table, {}), _jsx(TableFooter, { showingMessage: messages['authz.team.members.table.showing.users.text'] })] }));
|
|
105
143
|
};
|
|
106
144
|
export default TeamMembersTable;
|
|
107
145
|
//# sourceMappingURL=TeamMembersTable.js.map
|