+ default: ({
+ title,
+ description,
+ handleDelete,
+ }: {
+ title: string;
+ description: string;
+ handleDelete?: () => void;
+ }) => (
+
{title}
{description}
+ {handleDelete && (
+
+ {`delete-role-${title}`}
+
+ )}
),
}));
+jest.mock('./components/AssignNewRoleModal', () => ({
+ AssignNewRoleTrigger: () =>
Assign Role ,
+}));
+
describe('LibrariesUserManager', () => {
+ const mockMutate = jest.fn();
+ const defaultMockData = {
+ libraryId: 'lib:123',
+ permissions: [{ key: 'view' }, { key: 'reuse' }],
+ roles: [
+ {
+ role: 'admin',
+ name: 'Admin',
+ description: 'Administrator Role',
+ permissions: ['view', 'reuse'],
+ userCount: 5,
+ },
+ {
+ role: 'instructor',
+ name: 'Instructor',
+ description: 'Instructor Role',
+ permissions: ['view'],
+ userCount: 10,
+ },
+ ],
+ resources: [{ key: 'library', label: 'Library', description: '' }],
+ canManageTeam: true,
+ };
+
beforeEach(() => {
jest.clearAllMocks();
@@ -36,21 +85,7 @@ describe('LibrariesUserManager', () => {
(useParams as jest.Mock).mockReturnValue({ username: 'testuser' });
// Mock library authz context
- (useLibraryAuthZ as jest.Mock).mockReturnValue({
- libraryId: 'lib:123',
- permissions: [{ key: 'view' }, { key: 'reuse' }],
- roles: [
- {
- role: 'admin',
- name: 'Admin',
- description: 'Administrator Role',
- permissions: ['view', 'reuse'],
- },
- ],
- resources: [
- { key: 'library', label: 'Library', description: '' },
- ],
- });
+ (useLibraryAuthZ as jest.Mock).mockReturnValue(defaultMockData);
// Mock library data
(useLibrary as jest.Mock).mockReturnValue({
@@ -67,15 +102,31 @@ describe('LibrariesUserManager', () => {
{
username: 'testuser',
email: 'testuser@example.com',
- roles: ['admin'],
+ roles: ['admin', 'instructor'],
},
],
},
+ isLoading: false,
+ isFetching: false,
+ });
+
+ // Mock revoke user roles
+ (useRevokeUserRoles as jest.Mock).mockReturnValue({
+ mutate: mockMutate,
+ isPending: false,
});
});
+ const renderComponent = () => {
+ renderWrapper(
+
+
+ ,
+ );
+ };
+
it('renders the user roles correctly', () => {
- renderWrapper(
);
+ renderComponent();
// Breadcrumb check
expect(screen.getByText('Manage Access')).toBeInTheDocument();
@@ -85,8 +136,231 @@ describe('LibrariesUserManager', () => {
expect(screen.getByRole('heading', { level: 1 })).toHaveTextContent('testuser');
expect(screen.getByRole('paragraph')).toHaveTextContent('testuser@example.com');
- // RoleCard rendering
- expect(screen.getByTestId('role-card')).toHaveTextContent('Admin');
- expect(screen.getByTestId('role-card')).toHaveTextContent('Administrator Role');
+ expect(screen.getByText('Admin')).toBeInTheDocument();
+ expect(screen.getByText('Instructor')).toBeInTheDocument();
+
+ defaultMockData.roles.forEach((role) => {
+ expect(screen.getByText(role.name)).toBeInTheDocument();
+ expect(screen.getByText(role.description)).toBeInTheDocument();
+ });
+ });
+
+ it('renders assign role trigger when user has canManageTeam permission', () => {
+ renderComponent();
+
+ expect(screen.getByText('Assign Role')).toBeInTheDocument();
+ });
+
+ describe('Revoking User Role Flow', () => {
+ it('opens confirmation modal when delete role button is clicked', async () => {
+ const user = userEvent.setup();
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ });
+ });
+
+ it('displays correct confirmation modal content', async () => {
+ const user = userEvent.setup();
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ expect(screen.getByText(/Are you sure you want to remove the Admin role from/)).toBeInTheDocument();
+ expect(screen.getByText(/Test Library/)).toBeInTheDocument();
+ });
+ });
+
+ it('closes confirmation modal when cancel button is clicked', async () => {
+ const user = userEvent.setup();
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ });
+
+ const cancelButton = screen.getByText('Cancel');
+ await user.click(cancelButton);
+
+ await waitFor(() => {
+ expect(screen.queryByText('Remove role?')).not.toBeInTheDocument();
+ });
+ });
+
+ it('calls revokeUserRoles mutation when Remove button is clicked', async () => {
+ const user = userEvent.setup();
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ });
+
+ const removeButton = screen.getByText('Remove');
+ await user.click(removeButton);
+
+ expect(mockMutate).toHaveBeenCalledWith(
+ {
+ data: {
+ users: 'testuser',
+ role: 'admin',
+ scope: 'lib:123',
+ },
+ },
+ expect.objectContaining({
+ onSuccess: expect.any(Function),
+ onError: expect.any(Function),
+ }),
+ );
+ });
+
+ it('shows success toast when role is revoked successfully with multiple roles remaining', async () => {
+ const user = userEvent.setup();
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ });
+
+ const removeButton = screen.getByText('Remove');
+ await user.click(removeButton);
+
+ const onSuccessCallback = mockMutate.mock.calls[0][1].onSuccess;
+ onSuccessCallback();
+
+ await waitFor(() => {
+ expect(screen.getByText(/The Admin role has been successfully removed/)).toBeInTheDocument();
+ });
+ });
+
+ it('shows success toast with user removal message when last role is revoked', async () => {
+ const user = userEvent.setup();
+
+ (useTeamMembers as jest.Mock).mockReturnValue({
+ data: {
+ results: [
+ {
+ username: 'testuser',
+ email: 'testuser@example.com',
+ roles: ['admin'],
+ },
+ ],
+ },
+ isLoading: false,
+ isFetching: false,
+ });
+
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ });
+
+ const removeButton = screen.getByText('Remove');
+ await user.click(removeButton);
+
+ const onSuccessCallback = mockMutate.mock.calls[0][1].onSuccess;
+ onSuccessCallback();
+
+ await waitFor(() => {
+ expect(screen.getByText(/The user no longer has access to this library/)).toBeInTheDocument();
+ });
+ });
+
+ it('shows error toast when role revocation fails', async () => {
+ const user = userEvent.setup();
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ });
+
+ const removeButton = screen.getByText('Remove');
+ await user.click(removeButton);
+
+ const onErrorCallback = mockMutate.mock.calls[0][1].onError;
+ onErrorCallback(new Error('Network error'));
+
+ await waitFor(() => {
+ expect(screen.getByText(/Something went wrong on our end/)).toBeInTheDocument();
+ });
+ });
+
+ it('closes confirmation modal after successful role revocation', async () => {
+ const user = userEvent.setup();
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ });
+
+ const removeButton = screen.getByText('Remove');
+ await user.click(removeButton);
+
+ const onSuccessCallback = mockMutate.mock.calls[0][1].onSuccess;
+ onSuccessCallback();
+
+ await waitFor(() => {
+ expect(screen.queryByText('Remove role?')).not.toBeInTheDocument();
+ });
+ });
+
+ it('disables delete action when revocation is in progress', async () => {
+ const user = userEvent.setup();
+
+ (useRevokeUserRoles as jest.Mock).mockReturnValue({
+ mutate: mockMutate,
+ isPending: true,
+ });
+
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Admin');
+ await user.click(deleteButton);
+
+ expect(screen.queryByText('Remove role?')).not.toBeInTheDocument();
+ expect(mockMutate).not.toHaveBeenCalled();
+ });
+
+ it('passes correct context to confirmation modal', async () => {
+ const user = userEvent.setup();
+ renderComponent();
+
+ const deleteButton = screen.getByText('delete-role-Instructor');
+ await user.click(deleteButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Remove role?')).toBeInTheDocument();
+ });
+
+ const modal = screen.getByRole('dialog');
+ expect(within(modal).getByText(/Instructor role/)).toBeInTheDocument();
+ expect(within(modal).getByText(/testuser/)).toBeInTheDocument();
+ expect(within(modal).getByText(/Test Library/)).toBeInTheDocument();
+ });
});
});
diff --git a/src/authz-module/libraries-manager/LibrariesUserManager.tsx b/src/authz-module/libraries-manager/LibrariesUserManager.tsx
index e916f8e..1433a41 100644
--- a/src/authz-module/libraries-manager/LibrariesUserManager.tsx
+++ b/src/authz-module/libraries-manager/LibrariesUserManager.tsx
@@ -1,24 +1,39 @@
-import { useMemo } from 'react';
-import { useParams } from 'react-router-dom';
+import { useEffect, useMemo, useState } from 'react';
+import { useNavigate, useParams } from 'react-router-dom';
import { useIntl } from '@edx/frontend-platform/i18n';
+import { logError } from '@edx/frontend-platform/logging';
import { Container, Skeleton } from '@openedx/paragon';
import { ROUTES } from '@src/authz-module/constants';
+import { Role } from 'types';
+import { useToastManager } from '@src/authz-module/libraries-manager/ToastManagerContext';
import AuthZLayout from '../components/AuthZLayout';
import { useLibraryAuthZ } from './context';
import RoleCard from '../components/RoleCard';
import { AssignNewRoleTrigger } from './components/AssignNewRoleModal';
-import { useLibrary, useTeamMembers } from '../data/hooks';
+import ConfirmDeletionModal from './components/ConfirmDeletionModal';
+import { useLibrary, useRevokeUserRoles, useTeamMembers } from '../data/hooks';
import { buildPermissionMatrixByRole } from './utils';
import messages from './messages';
const LibrariesUserManager = () => {
const intl = useIntl();
+ const navigate = useNavigate();
const { username } = useParams();
const {
libraryId, permissions, roles, resources, canManageTeam,
} = useLibraryAuthZ();
+ const teamMembersPath = `/authz/${ROUTES.LIBRARIES_TEAM_PATH.replace(':libraryId', libraryId)}`;
+
+ useEffect(() => {
+ if (!canManageTeam) {
+ navigate(teamMembersPath);
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [canManageTeam]);
+
const { data: library } = useLibrary(libraryId);
+ const { mutate: revokeUserRoles, isPending: isRevokingUserRole } = useRevokeUserRoles();
const rootBreadcrumb = intl.formatMessage(messages['library.authz.breadcrumb.root']) || '';
const pageManageTitle = intl.formatMessage(messages['library.authz.manage.page.title']);
const querySettings = {
@@ -30,7 +45,13 @@ const LibrariesUserManager = () => {
sortBy: null,
};
- const { data: teamMember, isLoading: isLoadingTeamMember } = useTeamMembers(libraryId, querySettings);
+ const [roleToDelete, setRoleToDelete] = useState
(null);
+ const [showConfirmDeletionModal, setShowConfirmDeletionModal] = useState(false);
+ const { handleShowToast, handleDiscardToast } = useToastManager();
+
+ const {
+ data: teamMember, isLoading: isLoadingTeamMember, isFetching: isFetchingMember,
+ } = useTeamMembers(libraryId, querySettings);
const user = teamMember?.results?.find(member => member.username === username);
const userRoles = useMemo(() => {
@@ -40,11 +61,76 @@ const LibrariesUserManager = () => {
});
}, [roles, user?.roles, permissions, resources, intl]);
+ useEffect(() => {
+ if (!isFetchingMember) {
+ if (!isLoadingTeamMember && !user?.username) {
+ navigate(teamMembersPath);
+ }
+ }
+ // eslint-disable-next-line react-hooks/exhaustive-deps
+ }, [isFetchingMember, isLoadingTeamMember, user?.username]);
+
+ const handleCloseConfirmDeletionModal = () => {
+ setRoleToDelete(null);
+ setShowConfirmDeletionModal(false);
+ };
+
+ const handleShowConfirmDeletionModal = (role: Role) => {
+ if (isRevokingUserRole) { return; }
+
+ handleDiscardToast();
+ setRoleToDelete(role);
+ setShowConfirmDeletionModal(true);
+ };
+
+ const handleRevokeUserRole = () => {
+ if (!user || !roleToDelete) { return; }
+
+ const data = {
+ users: user.username,
+ role: roleToDelete.role,
+ scope: libraryId,
+ };
+
+ revokeUserRoles({ data }, {
+ onSuccess: () => {
+ const remainingRolesCount = userRoles.length - 1;
+ handleShowToast(intl.formatMessage(
+ messages['library.authz.team.remove.user.toast.success.description'],
+ {
+ role: roleToDelete.name,
+ rolesCount: remainingRolesCount,
+ },
+ ));
+ handleCloseConfirmDeletionModal();
+ },
+ onError: (error) => {
+ logError(error);
+ // eslint-disable-next-line react/no-unstable-nested-components
+ handleShowToast(intl.formatMessage(messages['library.authz.team.default.error.toast.message'], { b: chunk => {chunk} , br: () => }));
+ handleCloseConfirmDeletionModal();
+ },
+ });
+ };
+
return (
+
+
{user?.email}}
@@ -64,7 +150,7 @@ const LibrariesUserManager = () => {
title={role.name}
objectName={library.title}
description={role.description}
- showDelete
+ handleDelete={() => handleShowConfirmDeletionModal(role)}
permissionsByResource={role.resources as any[]}
/>
))}
diff --git a/src/authz-module/libraries-manager/ToastManagerContext.test.tsx b/src/authz-module/libraries-manager/ToastManagerContext.test.tsx
new file mode 100644
index 0000000..28a7547
--- /dev/null
+++ b/src/authz-module/libraries-manager/ToastManagerContext.test.tsx
@@ -0,0 +1,176 @@
+import { screen, waitFor, render as rtlRender } from '@testing-library/react';
+import userEvent from '@testing-library/user-event';
+import { IntlProvider } from '@edx/frontend-platform/i18n';
+import { ToastManagerProvider, useToastManager } from './ToastManagerContext';
+
+const render = (ui: React.ReactElement) => rtlRender(
+
+ {ui}
+ ,
+);
+
+const TestComponent = () => {
+ const { handleShowToast, handleDiscardToast } = useToastManager();
+
+ return (
+
+ handleShowToast('Test toast message')}>
+ Show Toast
+
+ handleShowToast('Another message')}>
+ Show Another Toast
+
+
+ Discard Toast
+
+
+ );
+};
+
+describe('ToastManagerContext', () => {
+ describe('ToastManagerProvider', () => {
+ it('does not show toast initially', () => {
+ render(
+
+
+ ,
+ );
+
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument();
+ });
+
+ it('shows toast when handleShowToast is called', async () => {
+ const user = userEvent.setup();
+ render(
+
+
+ ,
+ );
+
+ // handleShowToast is called on button click
+ const showButton = screen.getByText('Show Toast');
+ await user.click(showButton);
+
+ await waitFor(() => {
+ expect(screen.getByRole('alert')).toBeInTheDocument();
+ expect(screen.getByText('Test toast message')).toBeInTheDocument();
+ });
+ });
+
+ it('updates toast message when handleShowToast is called with different message', async () => {
+ const user = userEvent.setup();
+ render(
+
+
+ ,
+ );
+
+ // Show first toast
+ const showButton = screen.getByText('Show Toast');
+ await user.click(showButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Test toast message')).toBeInTheDocument();
+ });
+
+ // Show another toast
+ const showAnotherButton = screen.getByText('Show Another Toast');
+ await user.click(showAnotherButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Another message')).toBeInTheDocument();
+ expect(screen.queryByText('Test toast message')).not.toBeInTheDocument();
+ });
+ });
+
+ it('hides toast when handleDiscardToast is called', async () => {
+ const user = userEvent.setup();
+ render(
+
+
+ ,
+ );
+
+ const showButton = screen.getByText('Show Toast');
+ await user.click(showButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Test toast message')).toBeInTheDocument();
+ });
+
+ // handleDiscardToast is called on button click
+ const discardButton = screen.getByText('Discard Toast');
+ await user.click(discardButton);
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument();
+ });
+ });
+
+ it('hides toast when close button is clicked', async () => {
+ const user = userEvent.setup();
+ render(
+
+
+ ,
+ );
+
+ const showButton = screen.getByText('Show Toast');
+ await user.click(showButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Test toast message')).toBeInTheDocument();
+ });
+
+ const closeButton = screen.getByLabelText('Close');
+ await user.click(closeButton);
+
+ await waitFor(() => {
+ expect(screen.queryByRole('alert')).not.toBeInTheDocument();
+ });
+ });
+
+ it('calls handleClose callback when toast is closed', async () => {
+ const user = userEvent.setup();
+ const mockHandleClose = jest.fn();
+
+ render(
+
+
+ ,
+ );
+
+ const showButton = screen.getByText('Show Toast');
+ await user.click(showButton);
+
+ await waitFor(() => {
+ expect(screen.getByText('Test toast message')).toBeInTheDocument();
+ });
+
+ const closeButton = screen.getByLabelText('Close');
+ await user.click(closeButton);
+
+ await waitFor(() => {
+ expect(mockHandleClose).toHaveBeenCalledTimes(1);
+ });
+ });
+ });
+
+ describe('useToastManager hook', () => {
+ it('throws error when used outside ToastManagerProvider', () => {
+ // Suppress console.error for this test
+ const consoleSpy = jest.spyOn(console, 'error').mockImplementation(() => {});
+
+ const TestComponentWithoutProvider = () => {
+ useToastManager();
+ return Test
;
+ };
+
+ expect(() => {
+ render( );
+ }).toThrow('useToastManager must be used within an ToastManagerProvider');
+
+ consoleSpy.mockRestore();
+ });
+ });
+});
diff --git a/src/authz-module/libraries-manager/ToastManagerContext.tsx b/src/authz-module/libraries-manager/ToastManagerContext.tsx
new file mode 100644
index 0000000..7862140
--- /dev/null
+++ b/src/authz-module/libraries-manager/ToastManagerContext.tsx
@@ -0,0 +1,57 @@
+import {
+ createContext, useContext, useMemo, useState,
+} from 'react';
+import { Toast } from '@openedx/paragon';
+
+type ToastManagerContextType = {
+ handleShowToast: (message: string) => void;
+ handleDiscardToast: () => void;
+};
+
+const ToastManagerContext = createContext(undefined);
+
+interface ToastManagerProviderProps {
+ handleClose?: () => void
+ children: React.ReactNode | React.ReactNode[];
+}
+
+export const ToastManagerProvider = ({ handleClose, children }: ToastManagerProviderProps) => {
+ const [toastMessage, setToastMessage] = useState(null);
+
+ const handleShowToast = (message: string) => {
+ setToastMessage(message);
+ };
+
+ const handleDiscardToast = () => {
+ setToastMessage(null);
+ };
+
+ const value = useMemo((): ToastManagerContextType => ({
+ handleShowToast,
+ handleDiscardToast,
+ }), []);
+
+ return (
+
+ {children}
+
+ {
+ if (handleClose) { handleClose(); }
+ setToastMessage(null);
+ }}
+ show={!!toastMessage}
+ >
+ {toastMessage ?? ''}
+
+
+ );
+};
+
+export const useToastManager = (): ToastManagerContextType => {
+ const context = useContext(ToastManagerContext);
+ if (context === undefined) {
+ throw new Error('useToastManager must be used within an ToastManagerProvider');
+ }
+ return context;
+};
diff --git a/src/authz-module/libraries-manager/components/ConfirmDeletionModal.tsx b/src/authz-module/libraries-manager/components/ConfirmDeletionModal.tsx
new file mode 100644
index 0000000..2b9066c
--- /dev/null
+++ b/src/authz-module/libraries-manager/components/ConfirmDeletionModal.tsx
@@ -0,0 +1,74 @@
+import { FC } from 'react';
+import {
+ ActionRow, AlertModal, Icon, ModalDialog, Stack,
+ StatefulButton,
+} from '@openedx/paragon';
+import { useIntl } from '@edx/frontend-platform/i18n';
+
+import { SpinnerSimple } from '@openedx/paragon/icons';
+import messages from './messages';
+
+interface ConfirmDeletionModalProps {
+ isOpen: boolean;
+ close: () => void;
+ onSave: () => void;
+ isDeleting?: boolean;
+ context: {
+ userName: string;
+ scope: string;
+ role: string;
+ rolesCount: number;
+ }
+}
+
+const ConfirmDeletionModal: FC = ({
+ isOpen, close, onSave, isDeleting, context,
+}) => {
+ const intl = useIntl();
+ return (
+
+
+ {intl.formatMessage(messages['libraries.authz.manage.cancel.button'])}
+
+ ,
+ }}
+ state={isDeleting ? 'pending' : 'default'}
+ onClick={() => onSave()}
+ disabledStates={['pending']}
+ />
+
+ )}
+ isOverflowVisible={false}
+ >
+
+ {intl.formatMessage(messages['library.authz.team.remove.user.modal.body.1'], {
+ userName: context.userName,
+ scope: context.scope,
+ role: context.role,
+ })}
+
+ {context.rolesCount === 1 && (
+ {intl.formatMessage(messages['library.authz.team.remove.user.modal.body.2'])}
+ )}
+ {intl.formatMessage(messages['library.authz.team.remove.user.modal.body.3'])}
+
+
+
+ );
+};
+
+export default ConfirmDeletionModal;
diff --git a/src/authz-module/libraries-manager/components/messages.ts b/src/authz-module/libraries-manager/components/messages.ts
index d73643a..b69444f 100644
--- a/src/authz-module/libraries-manager/components/messages.ts
+++ b/src/authz-module/libraries-manager/components/messages.ts
@@ -6,11 +6,6 @@ const messages = defineMessages({
defaultMessage: 'Add New Role',
description: 'Libraries AuthZ assign a new role to a user button title',
},
- 'library.authz.manage.role.select.label': {
- id: 'library.authz.role.select.label',
- defaultMessage: 'Roles',
- description: 'Libraries team management label for roles select',
- },
'libraries.authz.manage.cancel.button': {
id: 'libraries.authz.manage.cancel.button',
defaultMessage: 'Cancel',
@@ -31,6 +26,41 @@ const messages = defineMessages({
defaultMessage: 'Role added successfully.',
description: 'Libraries AuthZ assign role success message',
},
+ 'library.authz.team.remove.user.modal.title': {
+ id: 'library.authz.team.remove.user.modal.title',
+ defaultMessage: 'Remove role?',
+ description: 'Libraries team management remove user modal title',
+ },
+ 'library.authz.team.remove.user.modal.body.1': {
+ id: 'library.authz.team.remove.user.modal.body',
+ defaultMessage: 'Are you sure you want to remove the {role} role from the user “{userName}” in the library {scope}?',
+ description: 'Libraries team management remove user modal body',
+ },
+ 'library.authz.team.remove.user.modal.body.2': {
+ id: 'library.authz.team.remove.user.modal.body',
+ defaultMessage: "This is the user's only role in this library. Removing it will revoke their access completely, and they will no longer appear in the library's member List.",
+ description: 'Libraries team management remove user modal body',
+ },
+ 'library.authz.team.remove.user.modal.body.3': {
+ id: 'library.authz.team.remove.user.modal.body',
+ defaultMessage: 'Are you sure you want to proceed?',
+ description: 'Libraries team management remove user modal body',
+ },
+ 'library.authz.manage.role.select.label': {
+ id: 'library.authz.role.select.label',
+ defaultMessage: 'Roles',
+ description: 'Libraries team management label for roles select',
+ },
+ 'libraries.authz.manage.removing.button': {
+ id: 'libraries.authz.manage.removing.button',
+ defaultMessage: 'Removing...',
+ description: 'Libraries AuthZ removing button title',
+ },
+ 'libraries.authz.manage.remove.button': {
+ id: 'libraries.authz.manage.remove.button',
+ defaultMessage: 'Remove',
+ description: 'Libraries AuthZ remove button title',
+ },
});
export default messages;
diff --git a/src/authz-module/libraries-manager/messages.ts b/src/authz-module/libraries-manager/messages.ts
index acba544..4e17426 100644
--- a/src/authz-module/libraries-manager/messages.ts
+++ b/src/authz-module/libraries-manager/messages.ts
@@ -26,6 +26,16 @@ const messages = defineMessages({
defaultMessage: 'Permissions',
description: 'Libreries AuthZ title for the permissions tab',
},
+ 'library.authz.team.remove.user.toast.success.description': {
+ id: 'library.authz.team.remove.user.toast.success.description',
+ 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 {}}',
+ description: 'Libraries team management remove user toast success',
+ },
+ 'library.authz.team.default.error.toast.message': {
+ id: 'library.authz.team.default.error.toast.message',
+ defaultMessage: 'Something went wrong on our end Please try again later.',
+ description: 'Libraries team management remove user toast success',
+ },
});
export default messages;