Skip to content

Commit 568828d

Browse files
refactor: moving files from libraries to authz module and minor improvements on the header
1 parent 74ee431 commit 568828d

19 files changed

Lines changed: 152 additions & 108 deletions

src/authz-module/components/AuthZLayout.tsx

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,10 @@ interface AuthZLayoutProps extends AuthZTitleProps {
1414
const AuthZLayout = ({ children, context, ...props }: AuthZLayoutProps) => (
1515
<>
1616
<StudioHeader
17-
number={context.id}
18-
org={context.org}
19-
title={context.title}
17+
number={context?.id || null}
18+
org={context?.org || null}
19+
title={context?.title || null}
20+
isHiddenMainMenu
2021
/>
2122
<AuthZTitle {...props} />
2223
{children}

src/authz-module/components/AuthZTitle.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -50,11 +50,11 @@ const AuthZTitle = ({
5050
/>
5151
<Row className="mt-4">
5252
<Col xs={12} md={7} className="mb-4">
53-
<div className="d-flex align-items-center">
53+
<div className="d-flex align-items-center flex-column-sm">
5454
<h2 className="text-primary mb-0">{pageTitle}</h2>
5555
{typeof pageSubtitle === 'string'
56-
? <><hr className="mx-lg-3" /><h3 className="mb-0 py-2 font-weight-light text-gray-700">{pageSubtitle}</h3></>
57-
: <><hr className="mx-lg-3" /> <div className="mb-0">{pageSubtitle}</div></>}
56+
? <> { pageSubtitle !== '' && <hr className="mx-lg-3" /> }<h3 className="mb-0 py-2 font-weight-light text-gray-700">{pageSubtitle}</h3></>
57+
: <>{ pageSubtitle !== '' && <hr className="mx-lg-3" /> } <div className="mb-0">{pageSubtitle}</div></>}
5858

5959
</div>
6060
</Col>

src/authz-module/constants.ts

Lines changed: 109 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,113 @@
1+
import { PermissionMetadata, ResourceMetadata, RoleMetadata } from 'types';
2+
3+
export const CONTENT_LIBRARY_PERMISSIONS = {
4+
DELETE_LIBRARY: 'content_libraries.delete_library',
5+
MANAGE_LIBRARY_TAGS: 'content_libraries.manage_library_tags',
6+
VIEW_LIBRARY: 'content_libraries.view_library',
7+
8+
EDIT_LIBRARY_CONTENT: 'content_libraries.edit_library_content',
9+
PUBLISH_LIBRARY_CONTENT: 'content_libraries.publish_library_content',
10+
REUSE_LIBRARY_CONTENT: 'content_libraries.reuse_library_content',
11+
12+
CREATE_LIBRARY_COLLECTION: 'content_libraries.create_library_collection',
13+
EDIT_LIBRARY_COLLECTION: 'content_libraries.edit_library_collection',
14+
DELETE_LIBRARY_COLLECTION: 'content_libraries.delete_library_collection',
15+
16+
MANAGE_LIBRARY_TEAM: 'content_libraries.manage_library_team',
17+
VIEW_LIBRARY_TEAM: 'content_libraries.view_library_team',
18+
};
19+
20+
export const CONTENT_COURSE_PERMISSIONS = {
21+
VIEW_COURSE: 'courses.view_course',
22+
CREATE_COURSE: 'courses.create_course',
23+
EDIT_COURSE_CONTENT: 'courses.edit_course_content',
24+
PUBLISH_COURSE_CONTENT: 'courses.publish_course_content',
25+
26+
REVIEW_COURSE_LIBRARY_UPDATES: 'courses.manage_library_updates',
27+
28+
VIEW_COURSE_UPDATES: 'courses.view_course_updates',
29+
MANAGE_COURSE_UPDATES: 'courses.manage_course_updates',
30+
31+
VIEW_COURSE_PAGES_RESOURCES: 'courses.view_pages_and_resources',
32+
MANAGE_COURSE_PAGES_RESOURCES: 'courses.manage_pages_and_resources',
33+
34+
VIEW_COURSE_FILES: 'courses.view_files',
35+
CREATE_COURSE_FILES: 'courses.create_files',
36+
EDIT_COURSE_FILES: 'courses.edit_files',
37+
DELETE_COURSE_FILES: 'courses.delete_files',
38+
39+
VIEW_COURSE_SCHEDULE: 'courses.view_schedule',
40+
EDIT_COURSE_SCHEDULE: 'courses.edit_schedule',
41+
VIEW_COURSE_DETAILS: 'courses.view_details',
42+
EDIT_COURSE_DETAILS: 'courses.edit_details',
43+
44+
VIEW_COURSE_GRADING_SETTINGS: 'courses.view_grading_settings',
45+
EDIT_COURSE_GRADING_SETTINGS: 'courses.edit_grading_settings',
46+
47+
VIEW_COURSE_TEAM: 'courses.view_course_team',
48+
MANAGE_COURSE_TEAM: 'courses.manage_course_team',
49+
MANAGE_COURSE_GROUP_CONFIGURATION: 'courses.manage_group_configurations',
50+
51+
MANAGE_COURSE_TAGS: 'courses.manage_tags',
52+
MANAGE_COURSE_TAXONOMIES: 'courses.manage_taxonomies',
53+
54+
MANAGE_COURSE_ADVANCED_SETTINGS: 'courses.manage_advanced_settings',
55+
MANAGE_COURSE_CERTIFICATES: 'courses.manage_certificates',
56+
57+
IMPORT_COURSE: 'courses.import_course',
58+
EXPORT_COURSE: 'courses.export_course',
59+
EXPORT_COURSE_TAGS: 'courses.export_tags',
60+
61+
VIEW_COURSE_CHECKLISTS: 'courses.view_checklists',
62+
VIEW_COURSE_GLOBAL_STAFF_SUPER_ADMINS: 'courses.view_global_staff_and_superadmins',
63+
};
64+
65+
// Note: this information will eventually come from the backend API
66+
// but for the MVP we decided to manage it in the frontend
67+
export const libraryRolesMetadata: RoleMetadata[] = [
68+
{ role: 'library_admin', name: 'Library Admin', description: 'The Library Admin has full control over the library, including managing users, modifying content, and handling publishing workflows. They ensure content is properly maintained and accessible as needed.' },
69+
{ role: 'library_author', name: 'Library Author', description: 'The Library Author is responsible for creating, editing, and publishing content within a library. They can manage tags and collections but cannot delete libraries or manage users.' },
70+
{ role: 'library_contributor', name: 'Library Contributor', description: 'The Library Contributor can create and edit content within a library but cannot publish it. They support the authoring process while leaving final publishing to Authors or Admins.' },
71+
{ role: 'library_user', name: 'Library User', description: 'The Library User can view and reuse content but cannot edit or delete any resource.' },
72+
];
73+
74+
export const libraryResourceTypes: ResourceMetadata[] = [
75+
{ key: 'library', label: 'Library', description: 'Permissions related to the library as a whole.' },
76+
{ key: 'library_content', label: 'Content', description: 'Permissions to create, edit, delete, and publish individual content items within the library.' },
77+
{ key: 'library_collection', label: 'Collection', description: 'Permissions to create, edit, and delete content collections within the library.' },
78+
{ key: 'library_team', label: 'Team', description: 'Permissions to manage user access and roles within the library.' },
79+
];
80+
81+
export const libraryPermissions: PermissionMetadata[] = [
82+
{ key: CONTENT_LIBRARY_PERMISSIONS.DELETE_LIBRARY, resource: 'library', description: 'Allows the user to delete the library and all its contents.' },
83+
{ key: CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TAGS, resource: 'library', description: 'Add or remove tags from content.' },
84+
{ key: CONTENT_LIBRARY_PERMISSIONS.VIEW_LIBRARY, resource: 'library', description: 'View content, search, filter, and sort within the library.' },
85+
86+
{ key: CONTENT_LIBRARY_PERMISSIONS.EDIT_LIBRARY_CONTENT, resource: 'library_content', description: 'Edit content in draft mode' },
87+
{ key: CONTENT_LIBRARY_PERMISSIONS.PUBLISH_LIBRARY_CONTENT, resource: 'library_content', description: 'Publish content, making it available for reuse' },
88+
{ key: CONTENT_LIBRARY_PERMISSIONS.REUSE_LIBRARY_CONTENT, resource: 'library_content', description: 'Reuse published content within a course.' },
89+
90+
{ key: CONTENT_LIBRARY_PERMISSIONS.CREATE_LIBRARY_COLLECTION, resource: 'library_collection', description: 'Create new collections within a library.' },
91+
{ key: CONTENT_LIBRARY_PERMISSIONS.EDIT_LIBRARY_COLLECTION, resource: 'library_collection', description: 'Add or remove content from existing collections.' },
92+
{ key: CONTENT_LIBRARY_PERMISSIONS.DELETE_LIBRARY_COLLECTION, resource: 'library_collection', description: 'Delete entire collections from the library.' },
93+
94+
{ key: CONTENT_LIBRARY_PERMISSIONS.MANAGE_LIBRARY_TEAM, resource: 'library_team', description: 'View the list of users who have access to the library.' },
95+
{ key: CONTENT_LIBRARY_PERMISSIONS.VIEW_LIBRARY_TEAM, resource: 'library_team', description: 'Add, remove, and assign roles to users within the library.' },
96+
];
97+
98+
export const DEFAULT_TOAST_DELAY = 5000;
99+
export const RETRY_TOAST_DELAY = 120_000; // 2 minutes
100+
export const SKELETON_ROWS = Array.from({ length: 10 }).map(() => ({
101+
username: 'skeleton',
102+
name: '',
103+
email: '',
104+
roles: [],
105+
}));
106+
1107
export const ROUTES = {
2108
LIBRARIES_TEAM_PATH: '/libraries/:libraryId',
3109
LIBRARIES_USER_PATH: '/libraries/:libraryId/:username',
110+
AUDIT_USER_PATH: '/user/:username',
4111
};
5112

6113
export enum RoleOperationErrorStatus {
@@ -10,3 +117,5 @@ export enum RoleOperationErrorStatus {
10117
ROLE_ASSIGNMENT_ERROR = 'role_assignment_error',
11118
ROLE_REMOVAL_ERROR = 'role_removal_error',
12119
}
120+
121+
export const TABLE_DEFAULT_PAGE_SIZE = 10;

src/authz-module/libraries-manager/components/TeamTable/hooks/useQuerySettings.test.ts renamed to src/authz-module/hooks/useQuerySettings.test.ts

File renamed without changes.

src/authz-module/libraries-manager/components/TeamTable/hooks/useQuerySettings.ts renamed to src/authz-module/hooks/useQuerySettings.ts

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -41,18 +41,18 @@ export const useQuerySettings = (
4141
const handleTableFetch = useCallback((tableFilters: DataTableFilters) => {
4242
setQuerySettings((prevSettings) => {
4343
// Extract filters
44-
const rolesFilter = tableFilters.filters.find((filter) => filter.id === 'roles')?.value?.join(',') ?? '';
45-
const searchFilter = tableFilters.filters.find((filter) => filter.id === 'username')?.value ?? '';
44+
const rolesFilter = tableFilters.filters?.find((filter) => filter.id === 'roles')?.value?.join(',') ?? '';
45+
const searchFilter = tableFilters.filters?.find((filter) => filter.id === 'username')?.value ?? '';
4646

4747
// Extract pagination
4848
const { pageSize = 10, pageIndex = 0 } = tableFilters;
4949

5050
// Extract and convert sorting
5151
let sortByOption = '';
5252
let sortByOrder = '';
53-
if (tableFilters.sortBy.length) {
54-
sortByOption = tableFilters.sortBy[0].id.replace(/([A-Z])/g, '_$1').toLowerCase();
55-
sortByOrder = tableFilters.sortBy[0].desc ? SortOrderKeys.DESC : SortOrderKeys.ASC;
53+
if (tableFilters.sortBy?.length) {
54+
sortByOption = tableFilters.sortBy[0]?.id.replace(/([A-Z])/g, '_$1').toLowerCase();
55+
sortByOrder = tableFilters.sortBy[0]?.desc ? SortOrderKeys.DESC : SortOrderKeys.ASC;
5656
}
5757

5858
const newQuerySettings: QuerySettings = {

src/authz-module/index.scss

Lines changed: 15 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,12 @@
11
@use "@openedx/paragon/styles/css/core/custom-media-breakpoints" as paragonCustomMediaBreakpoints;
22

3-
.authz-libraries {
3+
.authz-module {
44
--height-action-divider: 30px;
55

66
hr {
7-
border-top: var(--pgn-size-border-width) solid var(--pgn-color-border);
8-
width: 100%;
9-
}
10-
11-
@media (--pgn-size-breakpoint-min-width-lg) {
12-
hr {
13-
border-right: var(--pgn-size-border-width) solid var(--pgn-color-border);
14-
height: var(--height-action-divider);
15-
width: 0;
16-
}
7+
border-right: var(--pgn-size-border-width) solid var(--pgn-color-border);
8+
height: var(--height-action-divider);
9+
width: 0;
1710
}
1811

1912
.tab-content {
@@ -52,6 +45,17 @@
5245
line-height: 24px;
5346
}
5447
}
48+
49+
@media(--pgn-size-breakpoint-max-width-sm){
50+
.flex-column-sm {
51+
flex-direction: column;
52+
}
53+
hr {
54+
border-top: var(--pgn-size-border-width) solid var(--pgn-color-border);
55+
border-right: none;
56+
width: 100%;
57+
}
58+
}
5559
}
5660

5761
.toast-container {

src/authz-module/libraries-manager/LibrariesTeamManager.test.tsx

Lines changed: 2 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,11 @@ import { screen, within } from '@testing-library/react';
22
import userEvent from '@testing-library/user-event';
33
import { renderWrapper } from '@src/setupTest';
44
import { initializeMockApp } from '@edx/frontend-platform/testing';
5-
import { useLibrary, useUpdateLibrary } from '@src/authz-module/data/hooks';
5+
import { useLibrary } from '@src/authz-module/data/hooks';
66
import { useLibraryAuthZ } from './context';
77
import LibrariesTeamManager from './LibrariesTeamManager';
88
import { ToastManagerProvider } from './ToastManagerContext';
9-
import { CONTENT_LIBRARY_PERMISSIONS } from './constants';
9+
import { CONTENT_LIBRARY_PERMISSIONS } from '../constants';
1010

1111
jest.mock('./context', () => {
1212
const actual = jest.requireActual('./context');
@@ -21,7 +21,6 @@ const mockedUseLibraryAuthZ = useLibraryAuthZ as jest.Mock;
2121

2222
jest.mock('@src/authz-module/data/hooks', () => ({
2323
useLibrary: jest.fn(),
24-
useUpdateLibrary: jest.fn(),
2524
}));
2625

2726
jest.mock('./components/TeamTable', () => ({
@@ -91,10 +90,6 @@ describe('LibrariesTeamManager', () => {
9190
(useLibrary as jest.Mock).mockReturnValue({
9291
data: libraryData,
9392
});
94-
(useUpdateLibrary as jest.Mock).mockReturnValue({
95-
mutate,
96-
isPending: false,
97-
});
9893
});
9994

10095
it('renders tabs and layout content correctly', () => {

src/authz-module/libraries-manager/LibrariesTeamManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ const LibrariesTeamManager = () => {
4040
}, [roles, permissions, resources, intl]);
4141

4242
return (
43-
<div className="authz-libraries">
43+
<div className="authz-module">
4444
<AuthZLayout
4545
context={{ id: libraryId, title: library.title, org: library.org }}
4646
// Temporarily setting '/authz/libraries/:libraryId' as the URL for Manage Access breadcrumb for now as

src/authz-module/libraries-manager/LibrariesUserManager.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -131,7 +131,7 @@ const LibrariesUserManager = () => {
131131
};
132132

133133
return (
134-
<div className="authz-libraries">
134+
<div className="authz-module">
135135
<ConfirmDeletionModal
136136
isOpen={showConfirmDeletionModal}
137137
close={handleCloseConfirmDeletionModal}

src/authz-module/libraries-manager/ToastManagerContext.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { logError } from '@edx/frontend-platform/logging';
55
import { useIntl } from '@edx/frontend-platform/i18n';
66
import { Toast } from '@openedx/paragon';
77
import messages from './messages';
8-
import { DEFAULT_TOAST_DELAY, RETRY_TOAST_DELAY } from './constants';
8+
import { DEFAULT_TOAST_DELAY, RETRY_TOAST_DELAY } from '../constants';
99

1010
type ToastType = 'success' | 'error' | 'error-retry';
1111

0 commit comments

Comments
 (0)