feat: Created Course Team (#564)

This commit is contained in:
vladislavkeblysh
2023-08-23 16:21:43 +03:00
committed by GitHub
parent d768bfc97a
commit 2e8eed7504
56 changed files with 2925 additions and 12 deletions

View File

@@ -13,6 +13,7 @@ import FilesAndUploads from './files-and-uploads';
import { AdvancedSettings } from './advanced-settings';
import ScheduleAndDetails from './schedule-and-details';
import { GradingSettings } from './grading-settings';
import CourseTeam from './course-team/CourseTeam';
/**
* As of this writing, these routes are mounted at a path prefixed with the following:
@@ -94,10 +95,7 @@ const CourseAuthoringRoutes = ({ courseId }) => {
<GradingSettings courseId={courseId} />
</PageRoute>
<PageRoute path={`${path}/course_team`}>
{process.env.ENABLE_NEW_COURSE_TEAM_PAGE === 'true'
&& (
<Placeholder />
)}
<CourseTeam courseId={courseId} />
</PageRoute>
<PageRoute path={`${path}/settings/advanced`}>
<AdvancedSettings courseId={courseId} />

View File

@@ -7,3 +7,13 @@ export const STATEFUL_BUTTON_STATES = {
pending: 'pending',
default: 'default',
};
export const USER_ROLES = {
admin: 'instructor',
staff: 'staff',
};
export const BADGE_STATES = {
danger: 'danger',
secondary: 'secondary',
};

View File

@@ -0,0 +1,163 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl, injectIntl } from '@edx/frontend-platform/i18n';
import {
Button,
Container,
Layout,
} from '@edx/paragon';
import { Add as IconAdd } from '@edx/paragon/icons';
import InternetConnectionAlert from '../generic/internet-connection-alert';
import SubHeader from '../generic/sub-header/SubHeader';
import { USER_ROLES } from '../constants';
import messages from './messages';
import CourseTeamSideBar from './course-team-sidebar/CourseTeamSidebar';
import AddUserForm from './add-user-form/AddUserForm';
import AddTeamMember from './add-team-member/AddTeamMember';
import CourseTeamMember from './course-team-member/CourseTeamMember';
import InfoModal from './info-modal/InfoModal';
import { useCourseTeam } from './hooks';
const CourseTeam = ({ courseId }) => {
const intl = useIntl();
const {
modalType,
errorMessage,
courseName,
currentEmail,
courseTeamUsers,
currentUserEmail,
isLoading,
isSingleAdmin,
isFormVisible,
isQueryPending,
isAllowActions,
isInfoModalOpen,
isOwnershipHint,
isShowAddTeamMember,
isShowInitialSidebar,
isShowUserFilledSidebar,
isInternetConnectionAlertFailed,
openForm,
hideForm,
closeInfoModal,
handleAddUserSubmit,
handleOpenDeleteModal,
handleDeleteUserSubmit,
handleChangeRoleUserSubmit,
handleInternetConnectionFailed,
} = useCourseTeam({ intl, courseId });
if (isLoading) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <></>;
}
return (
<>
<Container size="xl" className="m-4">
<section className="course-team-container mb-4">
<Layout
lg={[{ span: 9 }, { span: 3 }]}
md={[{ span: 9 }, { span: 3 }]}
sm={[{ span: 9 }, { span: 3 }]}
xs={[{ span: 9 }, { span: 3 }]}
xl={[{ span: 9 }, { span: 3 }]}
>
<Layout.Element>
<article>
<div>
<SubHeader
title={intl.formatMessage(messages.headingTitle)}
subtitle={intl.formatMessage(messages.headingSubtitle)}
headerActions={isAllowActions && (
<Button
variant="outline-success"
iconBefore={IconAdd}
size="sm"
onClick={openForm}
disabled={isFormVisible}
>
{intl.formatMessage(messages.addNewMemberButton)}
</Button>
)}
/>
<section className="course-team-section">
<div className="members-container">
{isFormVisible && (
<AddUserForm
onSubmit={handleAddUserSubmit}
onCancel={hideForm}
/>
)}
{courseTeamUsers.length ? courseTeamUsers.map(({ username, role, email }) => (
<CourseTeamMember
key={email}
userName={username}
role={role}
email={email}
currentUserEmail={currentUserEmail || ''}
isAllowActions={isAllowActions}
isHideActions={role === USER_ROLES.admin && isSingleAdmin}
onChangeRole={handleChangeRoleUserSubmit}
onDelete={handleOpenDeleteModal}
/>
)) : null}
{isShowAddTeamMember && (
<AddTeamMember
onFormOpen={openForm}
isButtonDisable={isFormVisible}
/>
)}
</div>
{isShowInitialSidebar && (
<div className="sidebar-container">
<CourseTeamSideBar
courseId={courseId}
isOwnershipHint={isOwnershipHint}
isShowInitialSidebar={isShowInitialSidebar}
/>
</div>
)}
<InfoModal
isOpen={isInfoModalOpen}
close={closeInfoModal}
currentEmail={currentEmail}
errorMessage={errorMessage}
courseName={courseName}
modalType={modalType}
onDeleteSubmit={handleDeleteUserSubmit}
/>
</section>
</div>
</article>
</Layout.Element>
<Layout.Element>
{isShowUserFilledSidebar && (
<CourseTeamSideBar
courseId={courseId}
isOwnershipHint={isOwnershipHint}
/>
)}
</Layout.Element>
</Layout>
</section>
</Container>
<div className="alert-toast">
<InternetConnectionAlert
isFailed={isInternetConnectionAlertFailed}
isQueryPending={isQueryPending}
onInternetConnectionFailed={handleInternetConnectionFailed}
/>
</div>
</>
);
};
CourseTeam.propTypes = {
courseId: PropTypes.string.isRequired,
};
export default injectIntl(CourseTeam);

View File

@@ -0,0 +1,23 @@
@import "./course-team-sidebar/CourseTeamSidebar";
@import "./add-user-form/AddUserForm";
@import "./add-team-member/AddTeamMember";
@import "./course-team-member/CourseTeamMember";
.course-team-section {
.sidebar-container {
width: 30%;
.help-sidebar {
margin-top: 0;
hr {
display: none;
}
}
}
.members-container {
flex-grow: 1;
padding-top: 1.25rem;
}
}

View File

@@ -0,0 +1,222 @@
import React from 'react';
import {
render,
fireEvent,
cleanup,
waitFor,
} from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp } from '@edx/frontend-platform';
import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import initializeStore from '../store';
import { courseTeamMock, courseTeamWithOneUser, courseTeamWithoutUsers } from './__mocks__';
import { getCourseTeamApiUrl, updateCourseTeamUserApiUrl } from './data/api';
import CourseTeam from './CourseTeam';
import messages from './messages';
import { USER_ROLES } from '../constants';
import { executeThunk } from '../utils';
import { changeRoleTeamUserQuery, deleteCourseTeamQuery } from './data/thunk';
let axiosMock;
let store;
const mockPathname = '/foo-bar';
const courseId = '123';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
pathname: mockPathname,
}),
}));
const RootWrapper = () => (
<AppProvider store={store}>
<IntlProvider locale="en">
<CourseTeam courseId={courseId} />
</IntlProvider>
</AppProvider>
);
describe('<CourseTeam />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});
it('render CourseTeam component with 3 team members correctly', async () => {
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamMock);
const {
getByText, getByRole, getByTestId, queryAllByTestId,
} = render(<RootWrapper />);
await waitFor(() => {
expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument();
expect(getByRole('button', { name: messages.addNewMemberButton.defaultMessage })).toBeInTheDocument();
expect(getByTestId('course-team-sidebar')).toBeInTheDocument();
expect(queryAllByTestId('course-team-member')).toHaveLength(3);
});
});
it('render CourseTeam component with 1 team member correctly', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithOneUser);
const {
getByText, getByRole, getByTestId, getAllByTestId,
} = render(<RootWrapper />);
await waitFor(() => {
expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument();
expect(getByRole('button', { name: messages.addNewMemberButton.defaultMessage })).toBeInTheDocument();
expect(getByTestId('course-team-sidebar')).toBeInTheDocument();
expect(getAllByTestId('course-team-member')).toHaveLength(1);
});
});
it('render CourseTeam component without team member correctly', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithoutUsers);
const {
getByText, getByRole, getByTestId, queryAllByTestId,
} = render(<RootWrapper />);
await waitFor(() => {
expect(getByText(messages.headingTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.headingSubtitle.defaultMessage)).toBeInTheDocument();
expect(getByRole('button', { name: messages.addNewMemberButton.defaultMessage })).toBeInTheDocument();
expect(getByTestId('course-team-sidebar__initial')).toBeInTheDocument();
expect(queryAllByTestId('course-team-member')).toHaveLength(0);
});
});
it('render CourseTeam component with initial sidebar correctly', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithoutUsers);
const { getByTestId, queryByTestId } = render(<RootWrapper />);
await waitFor(() => {
expect(getByTestId('course-team-sidebar__initial')).toBeInTheDocument();
expect(queryByTestId('course-team-sidebar')).not.toBeInTheDocument();
});
});
it('render CourseTeam component without initial sidebar correctly', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamMock);
const { getByTestId, queryByTestId } = render(<RootWrapper />);
await waitFor(() => {
expect(queryByTestId('course-team-sidebar__initial')).not.toBeInTheDocument();
expect(getByTestId('course-team-sidebar')).toBeInTheDocument();
});
});
it('displays AddUserForm when clicking the "Add New Member" button', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithOneUser);
const { getByRole, queryByTestId } = render(<RootWrapper />);
await waitFor(() => {
expect(queryByTestId('add-user-form')).not.toBeInTheDocument();
const addButton = getByRole('button', { name: messages.addNewMemberButton.defaultMessage });
fireEvent.click(addButton);
expect(queryByTestId('add-user-form')).toBeInTheDocument();
});
});
it('displays AddUserForm when clicking the "Add a New Team member" button', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamWithOneUser);
const { getByRole, queryByTestId } = render(<RootWrapper />);
await waitFor(() => {
expect(queryByTestId('add-user-form')).not.toBeInTheDocument();
const addButton = getByRole('button', { name: 'Add a new team member' });
fireEvent.click(addButton);
expect(queryByTestId('add-user-form')).toBeInTheDocument();
});
});
it('not displays "Add New Member" and AddTeamMember component when isAllowActions is false', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, {
...courseTeamWithOneUser,
allowActions: false,
});
const { queryByRole, queryByTestId } = render(<RootWrapper />);
await waitFor(() => {
expect(queryByRole('button', { name: messages.addNewMemberButton.defaultMessage })).not.toBeInTheDocument();
expect(queryByTestId('add-team-member')).not.toBeInTheDocument();
});
});
it('should delete user', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamMock);
const { queryByText } = render(<RootWrapper />);
axiosMock
.onDelete(updateCourseTeamUserApiUrl(courseId, 'staff@example.com'))
.reply(200);
await executeThunk(deleteCourseTeamQuery(courseId, 'staff@example.com'), store.dispatch);
expect(queryByText('staff@example.com')).not.toBeInTheDocument();
});
it('should change role user', async () => {
cleanup();
axiosMock
.onGet(getCourseTeamApiUrl(courseId))
.reply(200, courseTeamMock);
const { getAllByText } = render(<RootWrapper />);
axiosMock
.onPut(updateCourseTeamUserApiUrl(courseId, 'staff@example.com', { role: USER_ROLES.admin }))
.reply(200, { role: USER_ROLES.admin });
await executeThunk(changeRoleTeamUserQuery(courseId, 'staff@example.com', { role: USER_ROLES.admin }), store.dispatch);
expect(getAllByText('Admin')).toHaveLength(1);
});
});

View File

@@ -0,0 +1,24 @@
module.exports = {
showTransferOwnershipHint: true,
users: [
{
email: 'staff@example.com',
id: '2',
role: 'staff',
username: 'Staff_Name',
},
{
email: 'audit@example.com',
id: '3',
role: 'staff',
username: 'Audit_Name',
},
{
email: 'vladislav.keblysh@raccoongang.com',
id: '1',
role: 'instructor',
username: 'Vladislav_Keblysh',
},
],
allowActions: true,
};

View File

@@ -0,0 +1,12 @@
module.exports = {
showTransferOwnershipHint: true,
users: [
{
email: 'staff@example.com',
id: '2',
role: 'staff',
username: 'Staff_Name',
},
],
allowActions: true,
};

View File

@@ -0,0 +1,5 @@
module.exports = {
showTransferOwnershipHint: true,
users: [],
allowActions: true,
};

View File

@@ -0,0 +1,3 @@
export { default as courseTeamMock } from './courseTeam';
export { default as courseTeamWithOneUser } from './courseTeamWithOneUser';
export { default as courseTeamWithoutUsers } from './courseTeamWithoutUsers';

View File

@@ -0,0 +1,39 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Add as IconAdd } from '@edx/paragon/icons';
import { Button } from '@edx/paragon';
import messages from './messages';
const AddTeamMember = ({ onFormOpen, isButtonDisable }) => {
const intl = useIntl();
return (
<div className="add-team-member bg-gray-100" data-testid="add-team-member">
<div className="add-team-member-info">
<h3 className="add-team-member-title font-weight-bold">{intl.formatMessage(messages.title)}</h3>
<span className="text-gray-500 small">{intl.formatMessage(messages.description)}</span>
</div>
<Button
variant="outline-success"
iconBefore={IconAdd}
onClick={onFormOpen}
disabled={isButtonDisable}
>
{intl.formatMessage(messages.button)}
</Button>
</div>
);
};
AddTeamMember.propTypes = {
onFormOpen: PropTypes.func.isRequired,
isButtonDisable: PropTypes.bool,
};
AddTeamMember.defaultProps = {
isButtonDisable: false,
};
export default AddTeamMember;

View File

@@ -0,0 +1,17 @@
.add-team-member {
display: flex;
align-items: center;
justify-content: space-between;
border: .0625rem solid $gray-200;
border-radius: .375rem;
box-shadow: inset inset 0 1px .125rem 1px $gray-200;
padding: 1.25rem 1.875rem;
.add-team-member-info {
width: 60%;
}
.add-team-member-title {
font-size: $spacer;
}
}

View File

@@ -0,0 +1,46 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import AddTeamMember from './AddTeamMember';
import messages from './messages';
const onFormOpenMock = jest.fn();
const renderComponent = (props) => render(
<IntlProvider locale="en">
<AddTeamMember onFormOpen={onFormOpenMock} {...props} />
</IntlProvider>,
);
describe('<AddTeamMember />', () => {
it('render AddTeamMember component correctly', () => {
const { getByText, getByRole } = renderComponent();
expect(getByText(messages.title.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.description.defaultMessage)).toBeInTheDocument();
expect(getByRole('button', { name: messages.button.defaultMessage })).toBeInTheDocument();
});
it('calls onFormOpen when the button is clicked', () => {
const { getByText } = renderComponent();
const addButton = getByText(messages.button.defaultMessage);
fireEvent.click(addButton);
expect(onFormOpenMock).toHaveBeenCalledTimes(1);
});
it('"Add a New Team member" button is disabled when isButtonDisable is true', () => {
const { getByRole } = renderComponent({ isButtonDisable: true });
const addButton = getByRole('button', { name: messages.button.defaultMessage });
expect(addButton).toBeDisabled();
});
it('"Add a New Team member" button is not disabled when isButtonDisable is false', () => {
const { getByRole } = renderComponent();
const addButton = getByRole('button', { name: messages.button.defaultMessage });
expect(addButton).not.toBeDisabled();
});
});

View File

@@ -0,0 +1,18 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
title: {
id: 'course-authoring.course-team.add-team-member.title',
defaultMessage: 'Add team members to this course',
},
description: {
id: 'course-authoring.course-team.add-team-member.description',
defaultMessage: 'Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.',
},
button: {
id: 'course-authoring.course-team.add-team-member.button',
defaultMessage: 'Add a new team member',
},
});
export default messages;

View File

@@ -0,0 +1,66 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import {
Button,
Form,
ActionRow,
} from '@edx/paragon';
import { Formik } from 'formik';
import messages from './messages';
import FormikControl from '../../generic/FormikControl';
import { EXAMPLE_USER_EMAIL } from '../constants';
const AddUserForm = ({ onSubmit, onCancel }) => {
const intl = useIntl();
return (
<div className="add-user-form" data-testid="add-user-form">
<Formik
initialValues={{ email: '' }}
onSubmit={onSubmit}
validateOnBlur
>
{({ handleSubmit, values }) => (
<>
<Form.Group size="sm" className="form-field">
<h3 className="form-title">{intl.formatMessage(messages.formTitle)}</h3>
<Form.Label size="sm" className="form-label font-weight-bold">
{intl.formatMessage(messages.formLabel)}
</Form.Label>
<FormikControl
name="email"
value={values.email}
placeholder={intl.formatMessage(messages.formPlaceholder, { email: EXAMPLE_USER_EMAIL })}
/>
<Form.Control.Feedback className="form-helper-text">
{intl.formatMessage(messages.formHelperText)}
</Form.Control.Feedback>
</Form.Group>
<ActionRow>
<Button variant="tertiary" size="sm" onClick={onCancel}>
{intl.formatMessage(messages.cancelButton)}
</Button>
<Button
size="sm"
onClick={handleSubmit}
disabled={!values.email.length}
type="submit"
>
{intl.formatMessage(messages.addUserButton)}
</Button>
</ActionRow>
</>
)}
</Formik>
</div>
);
};
AddUserForm.propTypes = {
onSubmit: PropTypes.func.isRequired,
onCancel: PropTypes.func.isRequired,
};
export default AddUserForm;

View File

@@ -0,0 +1,42 @@
.add-user-form {
display: flex;
flex-direction: column;
border: .0625rem solid $gray-200;
border-radius: .375rem;
box-shadow: 0 1px 1px $gray-200;
margin-bottom: 1.25rem;
background-color: $white;
.form-title {
font-size: 1.5rem;
margin-bottom: 1.875rem;
}
.form-field {
padding: 1.25rem 1.875rem;
margin-bottom: $spacer;
.pgn__form-group {
margin-bottom: 0;
}
}
.form-label {
position: relative;
&::after {
margin-left: .3125rem;
content: "*";
}
}
.form-helper-text {
font-size: $font-size-xs;
}
.pgn__action-row {
padding: $spacer 1.875rem;
border-top: .0625rem solid $gray-200;
justify-content: flex-start;
}
}

View File

@@ -0,0 +1,128 @@
import React from 'react';
import {
render,
fireEvent,
act,
waitFor,
} from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { AppProvider } from '@edx/frontend-platform/react';
import { initializeMockApp } from '@edx/frontend-platform';
import MockAdapter from 'axios-mock-adapter';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { EXAMPLE_USER_EMAIL } from '../constants';
import initializeStore from '../../store';
import { USER_ROLES } from '../../constants';
import { updateCourseTeamUserApiUrl } from '../data/api';
import { createCourseTeamQuery } from '../data/thunk';
import { executeThunk } from '../../utils';
import AddUserForm from './AddUserForm';
import messages from './messages';
let axiosMock;
let store;
const mockPathname = '/foo-bar';
const courseId = '123';
jest.mock('react-router-dom', () => ({
...jest.requireActual('react-router-dom'),
useLocation: () => ({
pathname: mockPathname,
}),
}));
const onSubmitMock = jest.fn();
const onCancelMock = jest.fn();
const RootWrapper = () => (
<AppProvider store={store}>
<IntlProvider locale="en">
<AddUserForm
onSubmit={onSubmitMock}
onCancel={onCancelMock}
/>
</IntlProvider>
</AppProvider>
);
describe('<AddUserForm />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
axiosMock = new MockAdapter(getAuthenticatedHttpClient());
});
it('render AddUserForm component correctly', () => {
const { getByText, getByPlaceholderText } = render(<RootWrapper />);
expect(getByText(messages.formTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.formLabel.defaultMessage)).toBeInTheDocument();
expect(getByPlaceholderText(messages.formPlaceholder.defaultMessage
.replace('{email}', EXAMPLE_USER_EMAIL))).toBeInTheDocument();
expect(getByText(messages.cancelButton.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.addUserButton.defaultMessage)).toBeInTheDocument();
});
it('calls onSubmit when the "Add User" button is clicked with a valid email', async () => {
const { getByPlaceholderText, getByRole } = render(<RootWrapper />);
const emailInput = getByPlaceholderText(messages.formPlaceholder.defaultMessage.replace('{email}', EXAMPLE_USER_EMAIL));
const addUserButton = getByRole('button', { name: messages.addUserButton.defaultMessage });
fireEvent.change(emailInput, { target: { value: EXAMPLE_USER_EMAIL } });
await act(async () => {
fireEvent.click(addUserButton);
});
await waitFor(() => {
expect(onSubmitMock).toHaveBeenCalledTimes(1);
expect(onSubmitMock).toHaveBeenCalledWith(
{ email: EXAMPLE_USER_EMAIL },
expect.objectContaining({ submitForm: expect.any(Function) }),
);
});
axiosMock
.onPost(updateCourseTeamUserApiUrl(courseId, EXAMPLE_USER_EMAIL), { role: USER_ROLES.staff })
.reply(200, { role: USER_ROLES.staff });
await executeThunk(createCourseTeamQuery(courseId, EXAMPLE_USER_EMAIL), store.dispatch);
});
it('calls onCancel when the "Cancel" button is clicked', () => {
const { getByText } = render(<RootWrapper />);
const cancelButton = getByText(messages.cancelButton.defaultMessage);
fireEvent.click(cancelButton);
expect(onCancelMock).toHaveBeenCalledTimes(1);
});
it('"Add User" button is disabled when the email input field is empty', () => {
const { getByText } = render(<RootWrapper />);
const addUserButton = getByText(messages.addUserButton.defaultMessage);
expect(addUserButton).toBeDisabled();
});
it('"Add User" button is not disabled when the email input field is not empty', () => {
const { getByPlaceholderText, getByText } = render(<RootWrapper />);
const emailInput = getByPlaceholderText(
messages.formPlaceholder.defaultMessage.replace('{email}', EXAMPLE_USER_EMAIL),
);
const addUserButton = getByText(messages.addUserButton.defaultMessage);
fireEvent.change(emailInput, { target: { value: 'user@example.com' } });
expect(addUserButton).not.toBeDisabled();
});
});

View File

@@ -0,0 +1,31 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
formTitle: {
id: 'course-authoring.course-team.form.title',
defaultMessage: 'Add a user to your course\'s team',
},
formLabel: {
id: 'course-authoring.course-team.form.label',
defaultMessage: 'User\'s email address',
},
formPlaceholder: {
id: 'course-authoring.course-team.form.placeholder',
defaultMessage: 'example: {email}',
},
formHelperText: {
id: 'course-authoring.course-team.form.helperText',
defaultMessage: 'Provide the email address of the user you want to add as Staff',
},
addUserButton: {
id: 'course-authoring.course-team.form.button.addUser',
defaultMessage: 'Add user',
},
cancelButton: {
id: 'course-authoring.course-team.form.button.cancel',
defaultMessage: 'Cancel',
},
});
export default messages;

View File

@@ -0,0 +1,7 @@
export const MODAL_TYPES = {
error: 'error',
delete: 'delete',
warning: 'warning',
};
export const EXAMPLE_USER_EMAIL = 'username@domain.com';

View File

@@ -0,0 +1,78 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Badge, Button, MailtoLink } from '@edx/paragon';
import { DeleteOutline as DeleteOutlineIcon } from '@edx/paragon/icons';
import messages from './messages';
import { USER_ROLES, BADGE_STATES } from '../../constants';
const CourseTeamMember = ({
userName,
role,
email,
onChangeRole,
onDelete,
currentUserEmail,
isHideActions,
isAllowActions,
}) => {
const intl = useIntl();
const isAdminRole = role === USER_ROLES.admin;
return (
<div className="course-team-member" data-testid="course-team-member">
<div className="member-info">
<Badge variant={isAdminRole ? BADGE_STATES.danger : BADGE_STATES.secondary} className="badge-current-user">
{isAdminRole
? intl.formatMessage(messages.roleAdmin)
: intl.formatMessage(messages.roleStaff)}
{currentUserEmail === email && (
<span className="badge-current-user">{intl.formatMessage(messages.roleYou)}</span>
)}
</Badge>
<span className="member-info-name font-weight-bold">{userName}</span>
<MailtoLink to={email}>{email}</MailtoLink>
</div>
{/* eslint-disable-next-line no-nested-ternary */}
{isAllowActions && (
!isHideActions ? (
<div className="member-actions">
<Button
variant={isAdminRole ? 'tertiary' : 'primary'}
size="sm"
onClick={() => onChangeRole(email, isAdminRole ? USER_ROLES.staff : USER_ROLES.admin)}
>
{isAdminRole ? intl.formatMessage(messages.removeButton) : intl.formatMessage(messages.addButton)}
</Button>
<Button
className="delete-button"
variant="tertiary"
size="sm"
data-testid="delete-button"
iconBefore={DeleteOutlineIcon}
onClick={() => onDelete(email)}
/>
</div>
) : (
<div className="member-hint text-right">
<span>{intl.formatMessage(messages.hint)}</span>
</div>
)
)}
</div>
);
};
CourseTeamMember.propTypes = {
userName: PropTypes.string.isRequired,
role: PropTypes.string.isRequired,
email: PropTypes.string.isRequired,
onChangeRole: PropTypes.func.isRequired,
onDelete: PropTypes.func.isRequired,
currentUserEmail: PropTypes.string.isRequired,
isHideActions: PropTypes.bool.isRequired,
isAllowActions: PropTypes.bool.isRequired,
};
export default CourseTeamMember;

View File

@@ -0,0 +1,63 @@
.course-team-container {
margin-top: 3rem;
}
.course-team-member {
display: flex;
align-items: center;
justify-content: space-between;
position: relative;
padding: 1.563rem 1.875rem 1.25rem;
background-color: $white;
border: .0625rem solid $gray-200;
border-radius: .375rem;
box-shadow: 0 1px 1px $gray-200;
&:not(:last-child) {
margin-bottom: 1.25rem;
}
.member-info {
position: relative;
display: flex;
flex-direction: column;
.badge {
position: absolute;
top: -2.25rem;
left: -.25rem;
}
.badge-current-user {
color: $gray-100;
margin-left: .25rem;
}
.member-info-name {
font-size: 1.5rem;
margin-bottom: .25rem;
}
}
.member-hint {
width: 45%;
margin-right: 3.875rem;
color: $gray-300;
font-size: $font-size-sm;
}
.member-actions {
display: flex;
gap: $spacer;
.delete-button {
display: flex;
align-items: center;
justify-content: center;
& > span {
margin: 0;
}
}
}
}

View File

@@ -0,0 +1,91 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { USER_ROLES } from '../../constants';
import CourseTeamMember from './CourseTeamMember';
import messages from './messages';
const userNameMock = 'User';
const emailMock = 'user@example.com';
const currentUserEmailMock = 'user@example.com';
const onChangeRoleMock = jest.fn();
const onDeleteMock = jest.fn();
const renderComponent = (props) => render(
<IntlProvider locale="en">
<CourseTeamMember
userName={userNameMock}
email={emailMock}
currentUserEmail="some@example.com"
onChangeRole={onChangeRoleMock}
onDelete={onDeleteMock}
isAllowActions
isHideActions={false}
role={USER_ROLES.admin}
{...props}
/>
</IntlProvider>,
);
describe('<CourseTeamMember />', () => {
it('render CourseTeamMember component correctly', () => {
const { getByText, getByRole, getByTestId } = renderComponent();
expect(getByText(userNameMock)).toBeInTheDocument();
expect(getByText(emailMock)).toBeInTheDocument();
expect(getByRole('button', { name: messages.removeButton.defaultMessage })).toBeInTheDocument();
expect(getByTestId('delete-button')).toBeInTheDocument();
expect(getByText(messages.roleAdmin.defaultMessage)).toBeInTheDocument();
});
it('displays correct badge and "You" label for the current user', () => {
const { getByText } = renderComponent({
role: USER_ROLES.staff,
currentUserEmail: currentUserEmailMock,
});
expect(getByText(messages.roleStaff.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.roleYou.defaultMessage)).toBeInTheDocument();
});
it('not displays actions when isAllowActions is false', () => {
const { queryByRole, queryByTestId } = renderComponent({
role: USER_ROLES.admin,
currentUserEmail: currentUserEmailMock,
isAllowActions: false,
});
expect(queryByRole('button', { name: messages.removeButton.defaultMessage })).not.toBeInTheDocument();
expect(queryByTestId('delete-button')).not.toBeInTheDocument();
});
it('calls onChangeRole when the "Add"/"Remove" button is clicked', () => {
const { getByRole } = renderComponent({
role: USER_ROLES.staff,
});
const addButton = getByRole('button', { name: messages.addButton.defaultMessage });
fireEvent.click(addButton);
expect(onChangeRoleMock).toHaveBeenCalledTimes(1);
expect(onChangeRoleMock).toHaveBeenCalledWith(emailMock, USER_ROLES.admin);
});
it('calls onDelete when the "Delete" button is clicked', () => {
const { getByTestId } = renderComponent();
const deleteButton = getByTestId('delete-button');
fireEvent.click(deleteButton);
expect(onDeleteMock).toHaveBeenCalledTimes(1);
});
it('renders the "Hint" when isHideActions is true', () => {
const { getByText, queryByRole, queryByTestId } = renderComponent({
isHideActions: true,
});
expect(queryByRole('button', { name: messages.addButton.defaultMessage })).not.toBeInTheDocument();
expect(queryByTestId('delete-button')).not.toBeInTheDocument();
expect(getByText(messages.hint.defaultMessage)).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,30 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
roleAdmin: {
id: 'course-authoring.course-team.member.role.admin',
defaultMessage: 'Admin',
},
roleStaff: {
id: 'course-authoring.course-team.member.role.staff',
defaultMessage: 'Staff',
},
roleYou: {
id: 'course-authoring.course-team.member.role.you',
defaultMessage: 'You!',
},
hint: {
id: 'course-authoring.course-team.member.hint',
defaultMessage: 'Promote another member to Admin to remove your admin rights',
},
addButton: {
id: 'course-authoring.course-team.member.button.add',
defaultMessage: 'Add admin access',
},
removeButton: {
id: 'course-authoring.course-team.member.button.remove',
defaultMessage: 'Remove admin access',
},
});
export default messages;

View File

@@ -0,0 +1,52 @@
import React from 'react';
import { render } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { initializeMockApp } from '@edx/frontend-platform';
import { AppProvider } from '@edx/frontend-platform/react';
import CourseTeamSidebar from './CourseTeamSidebar';
import messages from './messages';
import initializeStore from '../../store';
const courseId = 'course-123';
let store;
const renderComponent = (props) => render(
<AppProvider store={store} messages={{}}>
<IntlProvider locale="en">
<CourseTeamSidebar courseId={courseId} {...props} />
</IntlProvider>
</AppProvider>,
);
describe('<CourseTeamSidebar />', () => {
beforeEach(() => {
initializeMockApp({
authenticatedUser: {
userId: 3,
username: 'abc123',
administrator: true,
roles: [],
},
});
store = initializeStore();
});
it('render CourseTeamSidebar component correctly', () => {
const { getByText } = renderComponent();
expect(getByText(messages.sidebarTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.sidebarAbout_1.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.sidebarAbout_2.defaultMessage)).toBeInTheDocument();
expect(getByText(messages.sidebarAbout_3.defaultMessage)).toBeInTheDocument();
});
it('render CourseTeamSidebar when isOwnershipHint is true', () => {
const { getByText } = renderComponent({ isOwnershipHint: true });
expect(getByText(messages.ownershipTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(
'Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click to make another user the Admin, then ask that user to remove you from the Course Team list.',
)).toBeInTheDocument();
});
});

View File

@@ -0,0 +1,68 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import HelpSidebar from '../../generic/help-sidebar';
import messages from './messages';
const CourseTeamSideBar = ({ courseId, isOwnershipHint, isShowInitialSidebar }) => {
const intl = useIntl();
return (
<div
className="course-team-sidebar"
data-testid={isShowInitialSidebar ? 'course-team-sidebar__initial' : 'course-team-sidebar'}
>
<HelpSidebar
intl={intl}
courseId={courseId}
showOtherSettings={false}
>
<h4 className="help-sidebar-about-title">
{intl.formatMessage(messages.sidebarTitle)}
</h4>
<p className="help-sidebar-about-descriptions">
{intl.formatMessage(messages.sidebarAbout_1)}
</p>
<p className="help-sidebar-about-descriptions">
{intl.formatMessage(messages.sidebarAbout_2)}
</p>
<p className="help-sidebar-about-descriptions">
{intl.formatMessage(messages.sidebarAbout_3)}
</p>
</HelpSidebar>
{isOwnershipHint && (
<>
<hr />
<HelpSidebar
intl={intl}
courseId={courseId}
showOtherSettings={false}
>
<h4 className="help-sidebar-about-title">
{intl.formatMessage(messages.ownershipTitle)}
</h4>
<p className="help-sidebar-about-descriptions">
{intl.formatMessage(
messages.ownershipDescription,
{ strong: <strong>{intl.formatMessage(messages.addAdminAccess)}</strong> },
)}
</p>
</HelpSidebar>
</>
)}
</div>
);
};
CourseTeamSideBar.defaultProps = {
isShowInitialSidebar: false,
};
CourseTeamSideBar.propTypes = {
courseId: PropTypes.string.isRequired,
isOwnershipHint: PropTypes.bool.isRequired,
isShowInitialSidebar: PropTypes.bool,
};
export default CourseTeamSideBar;

View File

@@ -0,0 +1,11 @@
.course-team-sidebar {
.help-sidebar {
&:not(:first-child) {
margin-top: 0;
}
hr {
display: none;
}
}
}

View File

@@ -0,0 +1,34 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
sidebarTitle: {
id: 'course-authoring.course-team.sidebar.title',
defaultMessage: 'Course team roles',
},
sidebarAbout_1: {
id: 'course-authoring.course-team.sidebar.about-1',
defaultMessage: 'Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.',
},
sidebarAbout_2: {
id: 'course-authoring.course-team.sidebar.about-2',
defaultMessage: 'Admins are course team members who can add and remove other course team members.',
},
sidebarAbout_3: {
id: 'course-authoring.course-team.sidebar.about-3',
defaultMessage: 'All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.',
},
ownershipTitle: {
id: 'course-authoring.course-team.sidebar.ownership.title',
defaultMessage: 'Transferring ownership',
},
ownershipDescription: {
id: 'course-authoring.course-team.sidebar.ownership.description',
defaultMessage: 'Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.',
},
addAdminAccess: {
id: 'course-authoring.course-team.sidebar.ownership.addAdminAccess',
defaultMessage: 'Add admin access',
},
});
export default messages;

View File

@@ -0,0 +1,54 @@
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
import { USER_ROLES } from '../../constants';
const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;
export const getCourseTeamApiUrl = (courseId) => `${getApiBaseUrl()}/api/contentstore/v1/course_team/${courseId}`;
export const updateCourseTeamUserApiUrl = (courseId, email) => `${getApiBaseUrl()}/course_team/${courseId}/${email}`;
/**
* Get course team.
* @param {string} courseId
* @returns {Promise<Object>}
*/
export async function getCourseTeam(courseId) {
const { data } = await getAuthenticatedHttpClient()
.get(getCourseTeamApiUrl(courseId));
return camelCaseObject(data);
}
/**
* Create course team user.
* @param {string} courseId
* @param {string} email
* @returns {Promise<Object>}
*/
export async function createTeamUser(courseId, email) {
await getAuthenticatedHttpClient()
.post(updateCourseTeamUserApiUrl(courseId, email), { role: USER_ROLES.staff });
}
/**
* Change role course team user.
* @param {string} courseId
* @param {string} email
* @param {string} role
* @returns {Promise<Object>}
*/
export async function changeRoleTeamUser(courseId, email, role) {
await getAuthenticatedHttpClient()
.put(updateCourseTeamUserApiUrl(courseId, email), { role });
}
/**
* Delete course team user.
* @param {string} courseId
* @param {string} email
* @returns {Promise<Object>}
*/
export async function deleteTeamUser(courseId, email) {
await getAuthenticatedHttpClient()
.delete(updateCourseTeamUserApiUrl(courseId, email));
}

View File

@@ -0,0 +1,6 @@
export const getCourseTeamUsers = (state) => state.courseTeam.users;
export const getCourseTeamLoadingStatus = (state) => state.courseTeam.loadingCourseTeamStatus;
export const getErrorMessage = (state) => state.courseTeam.errorMessage;
export const getIsAllowActions = (state) => state.courseTeam.allowActions;
export const getIsOwnershipHint = (state) => state.courseTeam.showTransferOwnershipHint;
export const getSavingStatus = (state) => state.courseTeam.savingStatus;

View File

@@ -0,0 +1,46 @@
/* eslint-disable no-param-reassign */
import { createSlice } from '@reduxjs/toolkit';
import { RequestStatus } from '../../data/constants';
const slice = createSlice({
name: 'courseTeam',
initialState: {
loadingCourseTeamStatus: RequestStatus.IN_PROGRESS,
savingStatus: '',
users: [],
showTransferOwnershipHint: false,
allowActions: false,
errorMessage: '',
},
reducers: {
fetchCourseTeamSuccess: (state, { payload }) => {
state.users = payload.users;
state.showTransferOwnershipHint = payload.showTransferOwnershipHint;
state.allowActions = payload.allowActions;
},
updateLoadingCourseTeamStatus: (state, { payload }) => {
state.loadingCourseTeamStatus = payload.status;
},
deleteCourseTeamUser: (state, { payload }) => {
state.users = state.users.filter((user) => user.email !== payload);
},
updateSavingStatus: (state, { payload }) => {
state.savingStatus = payload.status;
},
setErrorMessage: (state, { payload }) => {
state.errorMessage = payload;
},
},
});
export const {
fetchCourseTeamSuccess,
updateLoadingCourseTeamStatus,
deleteCourseTeamUser,
updateSavingStatus,
setErrorMessage,
} = slice.actions;
export const {
reducer,
} = slice;

View File

@@ -0,0 +1,87 @@
import { RequestStatus } from '../../data/constants';
import {
getCourseTeam,
deleteTeamUser,
createTeamUser,
changeRoleTeamUser,
} from './api';
import {
fetchCourseTeamSuccess,
updateLoadingCourseTeamStatus,
deleteCourseTeamUser,
updateSavingStatus,
setErrorMessage,
} from './slice';
export function fetchCourseTeamQuery(courseId) {
return async (dispatch) => {
dispatch(updateLoadingCourseTeamStatus({ status: RequestStatus.IN_PROGRESS }));
try {
const courseTeam = await getCourseTeam(courseId);
dispatch(fetchCourseTeamSuccess(courseTeam));
dispatch(updateLoadingCourseTeamStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch (error) {
dispatch(updateLoadingCourseTeamStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}
export function createCourseTeamQuery(courseId, email) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.IN_PROGRESS }));
try {
await createTeamUser(courseId, email);
const courseTeam = await getCourseTeam(courseId);
dispatch(fetchCourseTeamSuccess(courseTeam));
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch (error) {
const message = error?.response?.data?.error || '';
dispatch(setErrorMessage(message));
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}
export function changeRoleTeamUserQuery(courseId, email, role) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.IN_PROGRESS }));
try {
await changeRoleTeamUser(courseId, email, role);
const courseTeam = await getCourseTeam(courseId);
dispatch(fetchCourseTeamSuccess(courseTeam));
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch ({ message }) {
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}
export function deleteCourseTeamQuery(courseId, email) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.IN_PROGRESS }));
try {
await deleteTeamUser(courseId, email);
dispatch(deleteCourseTeamUser(email));
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch (error) {
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}

139
src/course-team/hooks.jsx Normal file
View File

@@ -0,0 +1,139 @@
import { useDispatch, useSelector } from 'react-redux';
import { getAuthenticatedUser } from '@edx/frontend-platform/auth';
import { useEffect, useState } from 'react';
import { useToggle } from '@edx/paragon';
import { USER_ROLES } from '../constants';
import { RequestStatus } from '../data/constants';
import { useModel } from '../generic/model-store';
import {
changeRoleTeamUserQuery,
createCourseTeamQuery,
deleteCourseTeamQuery,
fetchCourseTeamQuery,
} from './data/thunk';
import {
getCourseTeamLoadingStatus,
getCourseTeamUsers,
getErrorMessage,
getIsAllowActions,
getIsOwnershipHint, getSavingStatus,
} from './data/selectors';
import { setErrorMessage } from './data/slice';
import { MODAL_TYPES } from './constants';
const useCourseTeam = ({ courseId }) => {
const dispatch = useDispatch();
const { email: currentUserEmail } = getAuthenticatedUser();
const courseDetails = useModel('courseDetails', courseId);
const [modalType, setModalType] = useState(MODAL_TYPES.delete);
const [isInfoModalOpen, openInfoModal, closeInfoModal] = useToggle(false);
const [isFormVisible, openForm, hideForm] = useToggle(false);
const [currentEmail, setCurrentEmail] = useState('');
const [isQueryPending, setIsQueryPending] = useState(false);
const courseTeamUsers = useSelector(getCourseTeamUsers);
const errorMessage = useSelector(getErrorMessage);
const savingStatus = useSelector(getSavingStatus);
const isAllowActions = useSelector(getIsAllowActions);
const isOwnershipHint = useSelector(getIsOwnershipHint);
const loadingCourseTeamStatus = useSelector(getCourseTeamLoadingStatus);
const isSingleAdmin = courseTeamUsers.filter((user) => user.role === USER_ROLES.admin).length === 1;
const handleOpenInfoModal = (type, email) => {
setCurrentEmail(email);
setModalType(type);
openInfoModal();
};
const handleCloseInfoModal = () => {
dispatch(setErrorMessage(''));
closeInfoModal();
};
const handleAddUserSubmit = (data) => {
setIsQueryPending(true);
const { email } = data;
const isUserContains = courseTeamUsers.some((user) => user.email === email);
if (isUserContains) {
handleOpenInfoModal(MODAL_TYPES.warning, email);
return;
}
dispatch(createCourseTeamQuery(courseId, email)).then((result) => {
if (result) {
hideForm();
dispatch(setErrorMessage(''));
return;
}
handleOpenInfoModal(MODAL_TYPES.error, email);
});
};
const handleDeleteUserSubmit = () => {
setIsQueryPending(true);
dispatch(deleteCourseTeamQuery(courseId, currentEmail));
handleCloseInfoModal();
};
const handleChangeRoleUserSubmit = (email, role) => {
setIsQueryPending(true);
dispatch(changeRoleTeamUserQuery(courseId, email, role));
};
const handleInternetConnectionFailed = () => {
setIsQueryPending(false);
};
const handleOpenDeleteModal = (email) => {
handleOpenInfoModal(MODAL_TYPES.delete, email);
};
useEffect(() => {
dispatch(fetchCourseTeamQuery(courseId));
}, [courseId]);
useEffect(() => {
if (savingStatus === RequestStatus.SUCCESSFUL) {
setIsQueryPending(false);
window.scrollTo({ top: 0, behavior: 'smooth' });
}
}, [savingStatus]);
return {
modalType,
errorMessage,
courseName: courseDetails?.name || '',
currentEmail,
courseTeamUsers,
currentUserEmail,
isLoading: loadingCourseTeamStatus === RequestStatus.IN_PROGRESS,
isSingleAdmin,
isFormVisible,
isAllowActions,
isInfoModalOpen,
isOwnershipHint,
isQueryPending,
isInternetConnectionAlertFailed: savingStatus === RequestStatus.FAILED,
isShowAddTeamMember: courseTeamUsers.length === 1 && isAllowActions,
isShowInitialSidebar: !courseTeamUsers.length && !isFormVisible,
isShowUserFilledSidebar: Boolean(courseTeamUsers.length) || isFormVisible,
openForm,
hideForm,
closeInfoModal,
handleAddUserSubmit,
handleOpenInfoModal,
handleOpenDeleteModal,
handleDeleteUserSubmit,
handleChangeRoleUserSubmit,
handleInternetConnectionFailed,
};
};
// eslint-disable-next-line import/prefer-default-export
export { useCourseTeam };

View File

@@ -0,0 +1,75 @@
import React from 'react';
import PropTypes from 'prop-types';
import {
ActionRow,
Button,
AlertModal,
} from '@edx/paragon';
import { useIntl } from '@edx/frontend-platform/i18n';
import { MODAL_TYPES } from '../constants';
import { getInfoModalSettings } from '../utils';
const InfoModal = ({
modalType,
isOpen,
close,
onDeleteSubmit,
currentEmail,
errorMessage,
courseName,
}) => {
const intl = useIntl();
const {
title,
message,
variant,
closeButtonText,
submitButtonText,
closeButtonVariant,
} = getInfoModalSettings(modalType, currentEmail, errorMessage, courseName, intl);
const isEmptyErrorMessage = modalType === MODAL_TYPES.error && !errorMessage;
return (
<AlertModal
title={title}
variant={variant}
isOpen={isOpen && !isEmptyErrorMessage}
onClose={close}
footerNode={(
<ActionRow>
<Button variant={closeButtonVariant} onClick={close}>
{closeButtonText}
</Button>
{modalType === MODAL_TYPES.delete && (
<Button
variant="danger"
onClick={(e) => {
e.preventDefault();
onDeleteSubmit();
}}
>
{submitButtonText}
</Button>
)}
</ActionRow>
)}
>
<p>{message}</p>
</AlertModal>
);
};
InfoModal.propTypes = {
modalType: PropTypes.string.isRequired,
isOpen: PropTypes.bool.isRequired,
close: PropTypes.func.isRequired,
currentEmail: PropTypes.string.isRequired,
errorMessage: PropTypes.string.isRequired,
courseName: PropTypes.string.isRequired,
onDeleteSubmit: PropTypes.func.isRequired,
};
export default InfoModal;

View File

@@ -0,0 +1,85 @@
import React from 'react';
import { render, fireEvent } from '@testing-library/react';
import { IntlProvider } from '@edx/frontend-platform/i18n';
import { MODAL_TYPES } from '../constants';
import InfoModal from './InfoModal';
import messages from './messages';
const closeMock = jest.fn();
const onDeleteSubmitMock = jest.fn();
const currentEmailMock = 'user@example.com';
const errorMessageMock = 'Error text error@example.com';
const courseNameMock = 'Course Name';
const renderComponent = (props) => render(
<IntlProvider locale="en">
<InfoModal
modalType={MODAL_TYPES.delete}
isOpen
close={closeMock}
onDeleteSubmit={onDeleteSubmitMock}
currentEmail={currentEmailMock}
errorMessage={errorMessageMock}
courseName={courseNameMock}
{...props}
/>
</IntlProvider>,
);
describe('<InfoModal />', () => {
it('render InfoModal component with type delete correctly', () => {
const { getByText, getByRole } = renderComponent({
modalType: MODAL_TYPES.delete,
});
expect(getByText(messages.deleteModalTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(
messages.deleteModalMessage.defaultMessage
.replace('{email}', currentEmailMock)
.replace('{courseName}', courseNameMock),
)).toBeInTheDocument();
expect(getByRole('button', { name: messages.deleteModalCancelButton.defaultMessage })).toBeInTheDocument();
expect(getByRole('button', { name: messages.deleteModalDeleteButton.defaultMessage })).toBeInTheDocument();
});
it('render InfoModal component with type error correctly', () => {
const { getByText, getByRole } = renderComponent({
modalType: MODAL_TYPES.error,
});
expect(getByText(messages.errorModalTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(errorMessageMock)).toBeInTheDocument();
expect(getByRole('button', { name: messages.errorModalOkButton.defaultMessage })).toBeInTheDocument();
});
it('render InfoModal component with type warning correctly', () => {
const { getByText, getByRole } = renderComponent({
modalType: MODAL_TYPES.warning,
});
expect(getByText(messages.warningModalTitle.defaultMessage)).toBeInTheDocument();
expect(getByText(
messages.warningModalMessage.defaultMessage
.replace('{email}', currentEmailMock)
.replace('{courseName}', courseNameMock),
)).toBeInTheDocument();
expect(getByRole('button', { name: messages.warningModalReturnButton.defaultMessage })).toBeInTheDocument();
});
it('calls close handler when the close button is clicked', () => {
const { getByRole } = renderComponent();
const closeButton = getByRole('button', { name: messages.deleteModalCancelButton.defaultMessage });
fireEvent.click(closeButton);
expect(closeMock).toHaveBeenCalledTimes(1);
});
it('calls onDeleteSubmit handler when the delete button is clicked', () => {
const { getByRole } = renderComponent();
const deleteButton = getByRole('button', { name: messages.deleteModalDeleteButton.defaultMessage });
fireEvent.click(deleteButton);
expect(onDeleteSubmitMock).toHaveBeenCalledTimes(1);
});
});

View File

@@ -0,0 +1,42 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
deleteModalTitle: {
id: 'course-authoring.course-team.member.button.remove',
defaultMessage: 'Are you sure?',
},
deleteModalMessage: {
id: 'course-authoring.course-team.delete-modal.message',
defaultMessage: 'Are you sure you want to delete {email} from the course team for “{courseName}”?',
},
deleteModalDeleteButton: {
id: 'course-authoring.course-team.delete-modal.button.delete',
defaultMessage: 'Delete',
},
deleteModalCancelButton: {
id: 'course-authoring.course-team.delete-modal.button.cancel',
defaultMessage: 'Cancel',
},
errorModalTitle: {
id: 'course-authoring.course-team.error-modal.title',
defaultMessage: 'Error adding user',
},
errorModalOkButton: {
id: 'course-authoring.course-team.error-modal.button.ok',
defaultMessage: 'Ok',
},
warningModalTitle: {
id: 'course-authoring.course-team.warning-modal.title',
defaultMessage: 'Already a course team member',
},
warningModalMessage: {
id: 'course-authoring.course-team.warning-modal.message',
defaultMessage: '{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.',
},
warningModalReturnButton: {
id: 'course-authoring.course-team.warning-modal.button.return',
defaultMessage: 'Return to team listing',
},
});
export default messages;

View File

@@ -0,0 +1,18 @@
import { defineMessages } from '@edx/frontend-platform/i18n';
const messages = defineMessages({
headingTitle: {
id: 'course-authoring.course-team.headingTitle',
defaultMessage: 'Course team',
},
headingSubtitle: {
id: 'course-authoring.course-team.subTitle',
defaultMessage: 'Settings',
},
addNewMemberButton: {
id: 'course-authoring.course-team.button.new-team-member',
defaultMessage: 'New team member',
},
});
export default messages;

53
src/course-team/utils.js Normal file
View File

@@ -0,0 +1,53 @@
import { MODAL_TYPES } from './constants';
import messages from './info-modal/messages';
/**
* Create an info modal settings dependent on modal type
* @param {typeof MODAL_TYPES} modalType - one of MODAL_TYPES
* @param {string} currentEmail - email in current user
* @param {string} errorEmail - email from wrong request
* @param {string} courseName - current course name
* @returns {{
* title: string,
* message: string,
* variant: string,
* closeButtonText: string,
* submitButtonText: string,
* closeButtonVariant: string
* }}
*/
const getInfoModalSettings = (modalType, currentEmail, errorMessage, courseName, intl) => {
switch (modalType) {
case MODAL_TYPES.delete:
return {
title: intl.formatMessage(messages.deleteModalTitle),
message: intl.formatMessage(messages.deleteModalMessage, { email: currentEmail, courseName }),
variant: 'danger',
closeButtonText: intl.formatMessage(messages.deleteModalCancelButton),
submitButtonText: intl.formatMessage(messages.deleteModalDeleteButton),
closeButtonVariant: 'tertiary',
};
case MODAL_TYPES.error:
return {
title: intl.formatMessage(messages.errorModalTitle),
message: errorMessage,
variant: 'danger',
closeButtonText: intl.formatMessage(messages.errorModalOkButton),
closeButtonVariant: 'danger',
};
case MODAL_TYPES.warning:
return {
title: intl.formatMessage(messages.warningModalTitle),
message: intl.formatMessage(messages.warningModalMessage, { email: currentEmail, courseName }),
variant: 'warning',
closeButtonText: intl.formatMessage(messages.warningModalReturnButton),
mainButtonVariant: 'primary',
};
default:
return '';
}
};
// eslint-disable-next-line import/prefer-default-export
export { getInfoModalSettings };

View File

@@ -39,7 +39,7 @@ const FormikControl = ({
};
FormikControl.propTypes = {
name: PropTypes.element.isRequired,
name: PropTypes.string.isRequired,
label: PropTypes.element,
help: PropTypes.element,
className: PropTypes.string,

View File

@@ -30,7 +30,10 @@ const InternetConnectionAlert = ({
useEffect(() => {
if (isQueryPending) {
onQueryProcessing();
if (onQueryProcessing) {
onQueryProcessing();
}
setShowAlert(!isOnline);
if (!isOnline) {
@@ -63,13 +66,13 @@ const InternetConnectionAlert = ({
InternetConnectionAlert.defaultProps = {
isQueryPending: false,
onQueryProcessing: null,
};
InternetConnectionAlert.propTypes = {
isFailed: PropTypes.bool.isRequired,
isQueryPending: PropTypes.bool,
onQueryProcessing: PropTypes.func.isRequired,
onQueryProcessing: PropTypes.func,
onInternetConnectionFailed: PropTypes.func.isRequired,
};

View File

@@ -2,7 +2,7 @@ import React from 'react';
import PropTypes from 'prop-types';
const SubHeader = ({
title, subtitle, contentTitle, description, instruction,
title, subtitle, contentTitle, description, instruction, headerActions,
}) => (
<>
<header className="sub-header">
@@ -10,6 +10,11 @@ const SubHeader = ({
<small className="sub-header-title-subtitle">{subtitle}</small>
{title}
</h2>
{headerActions && (
<div className="ml-auto sub-header-actions">
{headerActions}
</div>
)}
</header>
<header className="sub-header-content">
<h2 className="sub-header-content-title">{contentTitle}</h2>
@@ -20,12 +25,11 @@ const SubHeader = ({
)}
</>
);
SubHeader.defaultProps = {
instruction: '',
description: '',
headerActions: null,
};
SubHeader.propTypes = {
title: PropTypes.string.isRequired,
subtitle: PropTypes.string.isRequired,
@@ -35,6 +39,6 @@ SubHeader.propTypes = {
PropTypes.element,
PropTypes.string,
]),
headerActions: PropTypes.node,
};
export default SubHeader;

View File

@@ -1,3 +1,12 @@
.sub-header {
display: flex;
.sub-header-actions {
margin-bottom: 1.75rem;
align-self: flex-end;
}
}
.sub-header-title {
font: normal $font-weight-bold 2rem/2.25rem $font-family-base;
margin-bottom: 1.75rem;

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -606,6 +606,78 @@
"course-authoring.schedule-section.license.creative-commons.option.SA.label": "Share alike",
"course-authoring.schedule-section.license.creative-commons.option.SA.description": "Allow others to distribute derivative works only under a license identical to the license that governs your work. This option is incompatible with 'No Derivatives'.",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -640,5 +640,77 @@
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}",
"course-authoring.schedule.alert.button.saving": "Saving",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.course-team.headingTitle": "Course team",
"course-authoring.course-team.subTitle": "Settings",
"course-authoring.course-team.button.new-team-member": "New team member",
"course-authoring.course-team.sidebar.title": "Course team roles",
"course-authoring.course-team.sidebar.about-1": "Course team members with the Staff role are course co-authors. They have full writing and editing privileges on all course content.",
"course-authoring.course-team.sidebar.about-2": "Admins are course team members who can add and remove other course team members.",
"course-authoring.course-team.sidebar.about-3": "All course team members can access content in Studio, the LMS, and Insights, but are not automatically enrolled in the course.",
"course-authoring.course-team.sidebar.ownership.title": "Transferring ownership",
"course-authoring.course-team.sidebar.ownership.description": "Every course must have an Admin. If you are the Admin and you want to transfer ownership of the course, click {strong} to make another user the Admin, then ask that user to remove you from the Course Team list.",
"course-authoring.course-team.sidebar.ownership.addAdminAccess": "Add admin access",
"course-authoring.course-team.form.title": "Add a user to your course's team",
"course-authoring.course-team.form.label": "User's email address",
"course-authoring.course-team.form.placeholder": "example: {email}",
"course-authoring.course-team.form.helperText": "Provide the email address of the user you want to add as Staff",
"course-authoring.course-team.form.button.addUser": "Add user",
"course-authoring.course-team.form.button.cancel": "Cancel",
"course-authoring.course-team.add-team-member.title": "Add team members to this course",
"course-authoring.course-team.add-team-member.description": "Adding team members makes course authoring collaborative. Users must be signed up for Studio and have an active account.",
"course-authoring.course-team.add-team-member.button": "Add a new team member",
"course-authoring.course-team.member.role.admin": "Admin",
"course-authoring.course-team.member.role.staff": "Staff",
"course-authoring.course-team.member.role.you": "You!",
"course-authoring.course-team.member.hint": "Promote another member to Admin to remove your admin rights",
"course-authoring.course-team.member.button.add": "Add admin access",
"course-authoring.course-team.member.button.remove": "Remove admin access",
"course-authoring.course-team.delete-modal.title": "Are you sure?",
"course-authoring.course-team.delete-modal.message": "Are you sure you want to delete {email} from the course team for “{courseName}”?",
"course-authoring.course-team.delete-modal.button.delete": "Delete",
"course-authoring.course-team.delete-modal.button.cancel": "Cancel",
"course-authoring.course-team.error-modal.title": "Error adding user",
"course-authoring.course-team.error-modal.button.ok": "Ok",
"course-authoring.course-team.warning-modal.title": "Already a course team member",
"course-authoring.course-team.warning-modal.message": "{email} is already on the {courseName} team. Recheck the email address if you want to add a new member.",
"course-authoring.course-team.warning-modal.button.return": "Return to team listing",
"course-authoring.advanced-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.credit.eligibility.label": "Minimum credit-eligible grade:",
"course-authoring.grading-settings.credit.eligibility.description": "% Must be greater than or equal to the course passing grade",
"course-authoring.grading-settings.credit.eligibility.error.msg": "Not able to set passing grade to less than:",
"course-authoring.grading-settings.deadline.label": "Grace period on deadline:",
"course-authoring.grading-settings.deadline.description": "Leeway on due dates",
"course-authoring.grading-settings.alert.button.saving": "Saving",
"course-authoring.grading-settings.add-new-assignment-type.btn": "New assignment type",
"course-authoring.grading-settings.assignment-type.description": "Categories and labels for any exercises that are gradable",
"course-authoring.grading-settings.assignment-type.title": "Assignment types",
"course-authoring.grading-settings.grading-rules-policies.description": "Deadlines, requirements, and logistics around grading student work",
"course-authoring.grading-settings.grading-rules-policies.title": "Grading rules & policies",
"course-authoring.grading-settings.credit-eligibility.description": "Settings for course credit eligibility",
"course-authoring.grading-settings.credit-eligibility.title": "Credit eligibility",
"course-authoring.grading-settings.assignment.type-name.title": "Assignment type name",
"course-authoring.grading-settings.assignment.type-name.description": "The general category for this type of assignment, for example, Homework or Midterm Exam. This name is visible to learners.",
"course-authoring.grading-settings.assignment.type-name.error.message-1": "The assignment type must have a name.",
"course-authoring.grading-settings.assignment.type-name.error.message-3": "There's already another assignment type with this name.",
"course-authoring.grading-settings.assignment.abbreviation.title": "Abbreviation",
"course-authoring.grading-settings.assignment.abbreviation.description": "This short name for the assignment type (for example, HW or Midterm) appears next to assignments on a learner's Progress page.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.title": "Weight of total grade",
"course-authoring.grading-settings.assignment.weight-of-total-grade.description": "The weight of all assignments of this type as a percentage of the total grade, for example, 40. Do not include the percent symbol.",
"course-authoring.grading-settings.assignment.weight-of-total-grade.error.message": "Please enter an integer between 0 and 100.",
"course-authoring.grading-settings.assignment.total-number.title": "Total number",
"course-authoring.grading-settings.assignment.total-number.description": "The number of subsections in the course that contain problems of this assignment type.",
"course-authoring.grading-settings.assignment.total-number.error.message": "Please enter an integer greater than 0.",
"course-authoring.grading-settings.assignment.number-of-droppable.title": "Number of droppable",
"course-authoring.grading-settings.assignment.number-of-droppable.description": "The number of assignments of this type that will be dropped. The lowest scoring assignments are dropped first.",
"course-authoring.grading-settings.assignment.number-of-droppable.error.message": "Please enter non-negative integer.",
"course-authoring.grading-settings.assignment.alert.warning.description": "There are no assignments of this type in the course.",
"course-authoring.grading-settings.assignment.delete.button": "Delete",
"course-authoring.grading-settings.assignment.number-of-droppable.second.error.message": "Cannot drop more {type} assignments than are assigned.",
"course-authoring.grading-settings.assignment.alert.warning.usage.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.warning.title": "Warning: The number of {type} assignments defined here does not match the current number of {type} assignments in the course:",
"course-authoring.grading-settings.assignment.alert.success.title": "The number of {type} assignments in the course matches the number defined here.",
"course-authoring.grading-settings.assignment.type-name.error.message-2": "For grading to work, you must change all {initialAssignmentName} subsections to {value}"
}

View File

@@ -14,3 +14,4 @@
@import "generic/styles";
@import "schedule-and-details/ScheduleAndDetails";
@import "pages-and-resources/PagesAndResources";
@import "course-team/CourseTeam";

View File

@@ -10,6 +10,7 @@ import { reducer as gradingSettingsReducer } from './grading-settings/data/slice
import { reducer as scheduleAndDetailsReducer } from './schedule-and-details/data/slice';
import { reducer as liveReducer } from './pages-and-resources/live/data/slice';
import { reducer as filesReducer } from './files-and-uploads/data/slice';
import { reducer as courseTeamReducer } from './course-team/data/slice';
export default function initializeStore(preloadedState = undefined) {
return configureStore({
@@ -24,6 +25,7 @@ export default function initializeStore(preloadedState = undefined) {
gradingSettings: gradingSettingsReducer,
models: modelsReducer,
live: liveReducer,
courseTeam: courseTeamReducer,
},
preloadedState,
});