From 2e8eed7504b5b78156e96384b6ecd393f29ecd46 Mon Sep 17 00:00:00 2001 From: vladislavkeblysh <138868841+vladislavkeblysh@users.noreply.github.com> Date: Wed, 23 Aug 2023 16:21:43 +0300 Subject: [PATCH] feat: Created Course Team (#564) --- src/CourseAuthoringRoutes.jsx | 6 +- src/constants.js | 10 + src/course-team/CourseTeam.jsx | 163 +++++++++++++ src/course-team/CourseTeam.scss | 23 ++ src/course-team/CourseTeam.test.jsx | 222 ++++++++++++++++++ src/course-team/__mocks__/courseTeam.js | 24 ++ .../__mocks__/courseTeamWithOneUser.js | 12 + .../__mocks__/courseTeamWithoutUsers.js | 5 + src/course-team/__mocks__/index.js | 3 + .../add-team-member/AddTeamMember.jsx | 39 +++ .../add-team-member/AddTeamMember.scss | 17 ++ .../add-team-member/AddTeamMember.test.jsx | 46 ++++ src/course-team/add-team-member/messages.js | 18 ++ src/course-team/add-user-form/AddUserForm.jsx | 66 ++++++ .../add-user-form/AddUserForm.scss | 42 ++++ .../add-user-form/AddUserForm.test.jsx | 128 ++++++++++ src/course-team/add-user-form/messages.js | 31 +++ src/course-team/constants.js | 7 + .../course-team-member/CourseTeamMember.jsx | 78 ++++++ .../course-team-member/CourseTeamMember.scss | 63 +++++ .../CourseTeamMember.test.jsx | 91 +++++++ .../course-team-member/messages.js | 30 +++ .../CourseTeamSideBar.test.jsx | 52 ++++ .../course-team-sidebar/CourseTeamSidebar.jsx | 68 ++++++ .../CourseTeamSidebar.scss | 11 + .../course-team-sidebar/messages.js | 34 +++ src/course-team/data/api.js | 54 +++++ src/course-team/data/selectors.js | 6 + src/course-team/data/slice.js | 46 ++++ src/course-team/data/thunk.js | 87 +++++++ src/course-team/hooks.jsx | 139 +++++++++++ src/course-team/info-modal/InfoModal.jsx | 75 ++++++ src/course-team/info-modal/InfoModal.test.jsx | 85 +++++++ src/course-team/info-modal/messages.js | 42 ++++ src/course-team/messages.js | 18 ++ src/course-team/utils.js | 53 +++++ src/generic/FormikControl.jsx | 2 +- .../internet-connection-alert/index.jsx | 9 +- src/generic/sub-header/SubHeader.jsx | 12 +- src/generic/sub-header/SubHeader.scss | 9 + src/i18n/messages/ar.json | 72 ++++++ src/i18n/messages/de.json | 72 ++++++ src/i18n/messages/de_DE.json | 72 ++++++ src/i18n/messages/es_419.json | 72 ++++++ src/i18n/messages/fr.json | 72 ++++++ src/i18n/messages/fr_CA.json | 72 ++++++ src/i18n/messages/hi.json | 72 ++++++ src/i18n/messages/it.json | 72 ++++++ src/i18n/messages/it_IT.json | 72 ++++++ src/i18n/messages/pt.json | 72 ++++++ src/i18n/messages/pt_PT.json | 72 ++++++ src/i18n/messages/ru.json | 72 ++++++ src/i18n/messages/uk.json | 72 ++++++ src/i18n/messages/zh_CN.json | 72 ++++++ src/index.scss | 1 + src/store.js | 2 + 56 files changed, 2925 insertions(+), 12 deletions(-) create mode 100644 src/course-team/CourseTeam.jsx create mode 100644 src/course-team/CourseTeam.scss create mode 100644 src/course-team/CourseTeam.test.jsx create mode 100644 src/course-team/__mocks__/courseTeam.js create mode 100644 src/course-team/__mocks__/courseTeamWithOneUser.js create mode 100644 src/course-team/__mocks__/courseTeamWithoutUsers.js create mode 100644 src/course-team/__mocks__/index.js create mode 100644 src/course-team/add-team-member/AddTeamMember.jsx create mode 100644 src/course-team/add-team-member/AddTeamMember.scss create mode 100644 src/course-team/add-team-member/AddTeamMember.test.jsx create mode 100644 src/course-team/add-team-member/messages.js create mode 100644 src/course-team/add-user-form/AddUserForm.jsx create mode 100644 src/course-team/add-user-form/AddUserForm.scss create mode 100644 src/course-team/add-user-form/AddUserForm.test.jsx create mode 100644 src/course-team/add-user-form/messages.js create mode 100644 src/course-team/constants.js create mode 100644 src/course-team/course-team-member/CourseTeamMember.jsx create mode 100644 src/course-team/course-team-member/CourseTeamMember.scss create mode 100644 src/course-team/course-team-member/CourseTeamMember.test.jsx create mode 100644 src/course-team/course-team-member/messages.js create mode 100644 src/course-team/course-team-sidebar/CourseTeamSideBar.test.jsx create mode 100644 src/course-team/course-team-sidebar/CourseTeamSidebar.jsx create mode 100644 src/course-team/course-team-sidebar/CourseTeamSidebar.scss create mode 100644 src/course-team/course-team-sidebar/messages.js create mode 100644 src/course-team/data/api.js create mode 100644 src/course-team/data/selectors.js create mode 100644 src/course-team/data/slice.js create mode 100644 src/course-team/data/thunk.js create mode 100644 src/course-team/hooks.jsx create mode 100644 src/course-team/info-modal/InfoModal.jsx create mode 100644 src/course-team/info-modal/InfoModal.test.jsx create mode 100644 src/course-team/info-modal/messages.js create mode 100644 src/course-team/messages.js create mode 100644 src/course-team/utils.js diff --git a/src/CourseAuthoringRoutes.jsx b/src/CourseAuthoringRoutes.jsx index 29bb15ae1..854c0ee08 100644 --- a/src/CourseAuthoringRoutes.jsx +++ b/src/CourseAuthoringRoutes.jsx @@ -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 }) => { - {process.env.ENABLE_NEW_COURSE_TEAM_PAGE === 'true' - && ( - - )} + diff --git a/src/constants.js b/src/constants.js index aeb09d1a6..e0d0481bb 100644 --- a/src/constants.js +++ b/src/constants.js @@ -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', +}; diff --git a/src/course-team/CourseTeam.jsx b/src/course-team/CourseTeam.jsx new file mode 100644 index 000000000..d0a2ba573 --- /dev/null +++ b/src/course-team/CourseTeam.jsx @@ -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 ( + <> + +
+ + +
+
+ + {intl.formatMessage(messages.addNewMemberButton)} + + )} + /> +
+
+ {isFormVisible && ( + + )} + {courseTeamUsers.length ? courseTeamUsers.map(({ username, role, email }) => ( + + )) : null} + {isShowAddTeamMember && ( + + )} +
+ {isShowInitialSidebar && ( +
+ +
+ )} + +
+
+
+
+ + {isShowUserFilledSidebar && ( + + )} + +
+
+
+
+ +
+ + ); +}; + +CourseTeam.propTypes = { + courseId: PropTypes.string.isRequired, +}; + +export default injectIntl(CourseTeam); diff --git a/src/course-team/CourseTeam.scss b/src/course-team/CourseTeam.scss new file mode 100644 index 000000000..e6c973ff1 --- /dev/null +++ b/src/course-team/CourseTeam.scss @@ -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; + } +} diff --git a/src/course-team/CourseTeam.test.jsx b/src/course-team/CourseTeam.test.jsx new file mode 100644 index 000000000..4f3378874 --- /dev/null +++ b/src/course-team/CourseTeam.test.jsx @@ -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 = () => ( + + + + + +); + +describe('', () => { + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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); + }); +}); diff --git a/src/course-team/__mocks__/courseTeam.js b/src/course-team/__mocks__/courseTeam.js new file mode 100644 index 000000000..3a7d479a2 --- /dev/null +++ b/src/course-team/__mocks__/courseTeam.js @@ -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, +}; diff --git a/src/course-team/__mocks__/courseTeamWithOneUser.js b/src/course-team/__mocks__/courseTeamWithOneUser.js new file mode 100644 index 000000000..136e1f731 --- /dev/null +++ b/src/course-team/__mocks__/courseTeamWithOneUser.js @@ -0,0 +1,12 @@ +module.exports = { + showTransferOwnershipHint: true, + users: [ + { + email: 'staff@example.com', + id: '2', + role: 'staff', + username: 'Staff_Name', + }, + ], + allowActions: true, +}; diff --git a/src/course-team/__mocks__/courseTeamWithoutUsers.js b/src/course-team/__mocks__/courseTeamWithoutUsers.js new file mode 100644 index 000000000..357caf956 --- /dev/null +++ b/src/course-team/__mocks__/courseTeamWithoutUsers.js @@ -0,0 +1,5 @@ +module.exports = { + showTransferOwnershipHint: true, + users: [], + allowActions: true, +}; diff --git a/src/course-team/__mocks__/index.js b/src/course-team/__mocks__/index.js new file mode 100644 index 000000000..787cfc14d --- /dev/null +++ b/src/course-team/__mocks__/index.js @@ -0,0 +1,3 @@ +export { default as courseTeamMock } from './courseTeam'; +export { default as courseTeamWithOneUser } from './courseTeamWithOneUser'; +export { default as courseTeamWithoutUsers } from './courseTeamWithoutUsers'; diff --git a/src/course-team/add-team-member/AddTeamMember.jsx b/src/course-team/add-team-member/AddTeamMember.jsx new file mode 100644 index 000000000..427534dc0 --- /dev/null +++ b/src/course-team/add-team-member/AddTeamMember.jsx @@ -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 ( +
+
+

{intl.formatMessage(messages.title)}

+ {intl.formatMessage(messages.description)} +
+ +
+ ); +}; + +AddTeamMember.propTypes = { + onFormOpen: PropTypes.func.isRequired, + isButtonDisable: PropTypes.bool, +}; + +AddTeamMember.defaultProps = { + isButtonDisable: false, +}; + +export default AddTeamMember; diff --git a/src/course-team/add-team-member/AddTeamMember.scss b/src/course-team/add-team-member/AddTeamMember.scss new file mode 100644 index 000000000..6859a2dce --- /dev/null +++ b/src/course-team/add-team-member/AddTeamMember.scss @@ -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; + } +} diff --git a/src/course-team/add-team-member/AddTeamMember.test.jsx b/src/course-team/add-team-member/AddTeamMember.test.jsx new file mode 100644 index 000000000..bb20a93eb --- /dev/null +++ b/src/course-team/add-team-member/AddTeamMember.test.jsx @@ -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( + + + , +); + +describe('', () => { + 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(); + }); +}); diff --git a/src/course-team/add-team-member/messages.js b/src/course-team/add-team-member/messages.js new file mode 100644 index 000000000..3def09704 --- /dev/null +++ b/src/course-team/add-team-member/messages.js @@ -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; diff --git a/src/course-team/add-user-form/AddUserForm.jsx b/src/course-team/add-user-form/AddUserForm.jsx new file mode 100644 index 000000000..284288c86 --- /dev/null +++ b/src/course-team/add-user-form/AddUserForm.jsx @@ -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 ( +
+ + {({ handleSubmit, values }) => ( + <> + +

{intl.formatMessage(messages.formTitle)}

+ + {intl.formatMessage(messages.formLabel)} + + + + {intl.formatMessage(messages.formHelperText)} + +
+ + + + + + )} +
+
+ ); +}; + +AddUserForm.propTypes = { + onSubmit: PropTypes.func.isRequired, + onCancel: PropTypes.func.isRequired, +}; + +export default AddUserForm; diff --git a/src/course-team/add-user-form/AddUserForm.scss b/src/course-team/add-user-form/AddUserForm.scss new file mode 100644 index 000000000..0286372ca --- /dev/null +++ b/src/course-team/add-user-form/AddUserForm.scss @@ -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; + } +} diff --git a/src/course-team/add-user-form/AddUserForm.test.jsx b/src/course-team/add-user-form/AddUserForm.test.jsx new file mode 100644 index 000000000..d3716b14c --- /dev/null +++ b/src/course-team/add-user-form/AddUserForm.test.jsx @@ -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 = () => ( + + + + + +); + +describe('', () => { + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + + 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(); + }); +}); diff --git a/src/course-team/add-user-form/messages.js b/src/course-team/add-user-form/messages.js new file mode 100644 index 000000000..c61543b17 --- /dev/null +++ b/src/course-team/add-user-form/messages.js @@ -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; diff --git a/src/course-team/constants.js b/src/course-team/constants.js new file mode 100644 index 000000000..f6b603a17 --- /dev/null +++ b/src/course-team/constants.js @@ -0,0 +1,7 @@ +export const MODAL_TYPES = { + error: 'error', + delete: 'delete', + warning: 'warning', +}; + +export const EXAMPLE_USER_EMAIL = 'username@domain.com'; diff --git a/src/course-team/course-team-member/CourseTeamMember.jsx b/src/course-team/course-team-member/CourseTeamMember.jsx new file mode 100644 index 000000000..333be2ba2 --- /dev/null +++ b/src/course-team/course-team-member/CourseTeamMember.jsx @@ -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 ( +
+
+ + {isAdminRole + ? intl.formatMessage(messages.roleAdmin) + : intl.formatMessage(messages.roleStaff)} + {currentUserEmail === email && ( + {intl.formatMessage(messages.roleYou)} + )} + + {userName} + {email} +
+ {/* eslint-disable-next-line no-nested-ternary */} + {isAllowActions && ( + !isHideActions ? ( +
+ +
+ ) : ( +
+ {intl.formatMessage(messages.hint)} +
+ ) + )} +
+ ); +}; + +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; diff --git a/src/course-team/course-team-member/CourseTeamMember.scss b/src/course-team/course-team-member/CourseTeamMember.scss new file mode 100644 index 000000000..0285f13a9 --- /dev/null +++ b/src/course-team/course-team-member/CourseTeamMember.scss @@ -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; + } + } + } +} diff --git a/src/course-team/course-team-member/CourseTeamMember.test.jsx b/src/course-team/course-team-member/CourseTeamMember.test.jsx new file mode 100644 index 000000000..be6719ac6 --- /dev/null +++ b/src/course-team/course-team-member/CourseTeamMember.test.jsx @@ -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( + + + , +); + +describe('', () => { + 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(); + }); +}); diff --git a/src/course-team/course-team-member/messages.js b/src/course-team/course-team-member/messages.js new file mode 100644 index 000000000..784319b35 --- /dev/null +++ b/src/course-team/course-team-member/messages.js @@ -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; diff --git a/src/course-team/course-team-sidebar/CourseTeamSideBar.test.jsx b/src/course-team/course-team-sidebar/CourseTeamSideBar.test.jsx new file mode 100644 index 000000000..7218ba40b --- /dev/null +++ b/src/course-team/course-team-sidebar/CourseTeamSideBar.test.jsx @@ -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( + + + + + , +); + +describe('', () => { + 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(); + }); +}); diff --git a/src/course-team/course-team-sidebar/CourseTeamSidebar.jsx b/src/course-team/course-team-sidebar/CourseTeamSidebar.jsx new file mode 100644 index 000000000..7da9be0f2 --- /dev/null +++ b/src/course-team/course-team-sidebar/CourseTeamSidebar.jsx @@ -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 ( +
+ +

+ {intl.formatMessage(messages.sidebarTitle)} +

+

+ {intl.formatMessage(messages.sidebarAbout_1)} +

+

+ {intl.formatMessage(messages.sidebarAbout_2)} +

+

+ {intl.formatMessage(messages.sidebarAbout_3)} +

+
+ {isOwnershipHint && ( + <> +
+ +

+ {intl.formatMessage(messages.ownershipTitle)} +

+

+ {intl.formatMessage( + messages.ownershipDescription, + { strong: {intl.formatMessage(messages.addAdminAccess)} }, + )} +

+
+ + )} +
+ ); +}; + +CourseTeamSideBar.defaultProps = { + isShowInitialSidebar: false, +}; + +CourseTeamSideBar.propTypes = { + courseId: PropTypes.string.isRequired, + isOwnershipHint: PropTypes.bool.isRequired, + isShowInitialSidebar: PropTypes.bool, +}; + +export default CourseTeamSideBar; diff --git a/src/course-team/course-team-sidebar/CourseTeamSidebar.scss b/src/course-team/course-team-sidebar/CourseTeamSidebar.scss new file mode 100644 index 000000000..42a8acf52 --- /dev/null +++ b/src/course-team/course-team-sidebar/CourseTeamSidebar.scss @@ -0,0 +1,11 @@ +.course-team-sidebar { + .help-sidebar { + &:not(:first-child) { + margin-top: 0; + } + + hr { + display: none; + } + } +} diff --git a/src/course-team/course-team-sidebar/messages.js b/src/course-team/course-team-sidebar/messages.js new file mode 100644 index 000000000..a1934c19b --- /dev/null +++ b/src/course-team/course-team-sidebar/messages.js @@ -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; diff --git a/src/course-team/data/api.js b/src/course-team/data/api.js new file mode 100644 index 000000000..321671600 --- /dev/null +++ b/src/course-team/data/api.js @@ -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} + */ +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} + */ +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} + */ +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} + */ +export async function deleteTeamUser(courseId, email) { + await getAuthenticatedHttpClient() + .delete(updateCourseTeamUserApiUrl(courseId, email)); +} diff --git a/src/course-team/data/selectors.js b/src/course-team/data/selectors.js new file mode 100644 index 000000000..99c602fda --- /dev/null +++ b/src/course-team/data/selectors.js @@ -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; diff --git a/src/course-team/data/slice.js b/src/course-team/data/slice.js new file mode 100644 index 000000000..374210b03 --- /dev/null +++ b/src/course-team/data/slice.js @@ -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; diff --git a/src/course-team/data/thunk.js b/src/course-team/data/thunk.js new file mode 100644 index 000000000..78012870f --- /dev/null +++ b/src/course-team/data/thunk.js @@ -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; + } + }; +} diff --git a/src/course-team/hooks.jsx b/src/course-team/hooks.jsx new file mode 100644 index 000000000..6fceb5aac --- /dev/null +++ b/src/course-team/hooks.jsx @@ -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 }; diff --git a/src/course-team/info-modal/InfoModal.jsx b/src/course-team/info-modal/InfoModal.jsx new file mode 100644 index 000000000..4b9f5a995 --- /dev/null +++ b/src/course-team/info-modal/InfoModal.jsx @@ -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 ( + + + {modalType === MODAL_TYPES.delete && ( + + )} + + )} + > +

{message}

+
+ ); +}; + +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; diff --git a/src/course-team/info-modal/InfoModal.test.jsx b/src/course-team/info-modal/InfoModal.test.jsx new file mode 100644 index 000000000..f97f8a0ba --- /dev/null +++ b/src/course-team/info-modal/InfoModal.test.jsx @@ -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( + + + , +); + +describe('', () => { + 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); + }); +}); diff --git a/src/course-team/info-modal/messages.js b/src/course-team/info-modal/messages.js new file mode 100644 index 000000000..9a3dd74be --- /dev/null +++ b/src/course-team/info-modal/messages.js @@ -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; diff --git a/src/course-team/messages.js b/src/course-team/messages.js new file mode 100644 index 000000000..c6a6ebc05 --- /dev/null +++ b/src/course-team/messages.js @@ -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; diff --git a/src/course-team/utils.js b/src/course-team/utils.js new file mode 100644 index 000000000..53604cf72 --- /dev/null +++ b/src/course-team/utils.js @@ -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 }; diff --git a/src/generic/FormikControl.jsx b/src/generic/FormikControl.jsx index 2d2ab5e7d..981e3be02 100644 --- a/src/generic/FormikControl.jsx +++ b/src/generic/FormikControl.jsx @@ -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, diff --git a/src/generic/internet-connection-alert/index.jsx b/src/generic/internet-connection-alert/index.jsx index fbf8be548..6795f1387 100644 --- a/src/generic/internet-connection-alert/index.jsx +++ b/src/generic/internet-connection-alert/index.jsx @@ -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, }; diff --git a/src/generic/sub-header/SubHeader.jsx b/src/generic/sub-header/SubHeader.jsx index f6a7608a6..121326be5 100644 --- a/src/generic/sub-header/SubHeader.jsx +++ b/src/generic/sub-header/SubHeader.jsx @@ -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, }) => ( <>
@@ -10,6 +10,11 @@ const SubHeader = ({ {subtitle} {title} + {headerActions && ( +
+ {headerActions} +
+ )}

{contentTitle}

@@ -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; diff --git a/src/generic/sub-header/SubHeader.scss b/src/generic/sub-header/SubHeader.scss index 3a52f6768..f1c5ff267 100644 --- a/src/generic/sub-header/SubHeader.scss +++ b/src/generic/sub-header/SubHeader.scss @@ -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; diff --git a/src/i18n/messages/ar.json b/src/i18n/messages/ar.json index 0c0fbc5c5..d16a0cea9 100644 --- a/src/i18n/messages/ar.json +++ b/src/i18n/messages/ar.json @@ -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}" } diff --git a/src/i18n/messages/de.json b/src/i18n/messages/de.json index 064cd4855..435a0a818 100644 --- a/src/i18n/messages/de.json +++ b/src/i18n/messages/de.json @@ -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}" } diff --git a/src/i18n/messages/de_DE.json b/src/i18n/messages/de_DE.json index 21731f205..f5e78c336 100644 --- a/src/i18n/messages/de_DE.json +++ b/src/i18n/messages/de_DE.json @@ -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}" } diff --git a/src/i18n/messages/es_419.json b/src/i18n/messages/es_419.json index 6f9d298b9..85458e389 100644 --- a/src/i18n/messages/es_419.json +++ b/src/i18n/messages/es_419.json @@ -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}" } diff --git a/src/i18n/messages/fr.json b/src/i18n/messages/fr.json index 52118e6d5..84a01ce15 100644 --- a/src/i18n/messages/fr.json +++ b/src/i18n/messages/fr.json @@ -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}" } diff --git a/src/i18n/messages/fr_CA.json b/src/i18n/messages/fr_CA.json index f56ed3319..4f52c1b31 100644 --- a/src/i18n/messages/fr_CA.json +++ b/src/i18n/messages/fr_CA.json @@ -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}" } diff --git a/src/i18n/messages/hi.json b/src/i18n/messages/hi.json index 064cd4855..435a0a818 100644 --- a/src/i18n/messages/hi.json +++ b/src/i18n/messages/hi.json @@ -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}" } diff --git a/src/i18n/messages/it.json b/src/i18n/messages/it.json index 064cd4855..435a0a818 100644 --- a/src/i18n/messages/it.json +++ b/src/i18n/messages/it.json @@ -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}" } diff --git a/src/i18n/messages/it_IT.json b/src/i18n/messages/it_IT.json index 4c05f1407..4a00f3c66 100644 --- a/src/i18n/messages/it_IT.json +++ b/src/i18n/messages/it_IT.json @@ -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}" } diff --git a/src/i18n/messages/pt.json b/src/i18n/messages/pt.json index 064cd4855..435a0a818 100644 --- a/src/i18n/messages/pt.json +++ b/src/i18n/messages/pt.json @@ -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}" } diff --git a/src/i18n/messages/pt_PT.json b/src/i18n/messages/pt_PT.json index 8c9d0a696..d9456a708 100644 --- a/src/i18n/messages/pt_PT.json +++ b/src/i18n/messages/pt_PT.json @@ -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}" } diff --git a/src/i18n/messages/ru.json b/src/i18n/messages/ru.json index 064cd4855..b072f4611 100644 --- a/src/i18n/messages/ru.json +++ b/src/i18n/messages/ru.json @@ -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:", diff --git a/src/i18n/messages/uk.json b/src/i18n/messages/uk.json index 064cd4855..435a0a818 100644 --- a/src/i18n/messages/uk.json +++ b/src/i18n/messages/uk.json @@ -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}" } diff --git a/src/i18n/messages/zh_CN.json b/src/i18n/messages/zh_CN.json index 064cd4855..435a0a818 100644 --- a/src/i18n/messages/zh_CN.json +++ b/src/i18n/messages/zh_CN.json @@ -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}" } diff --git a/src/index.scss b/src/index.scss index 83c5dac16..91ee8d0ce 100755 --- a/src/index.scss +++ b/src/index.scss @@ -14,3 +14,4 @@ @import "generic/styles"; @import "schedule-and-details/ScheduleAndDetails"; @import "pages-and-resources/PagesAndResources"; +@import "course-team/CourseTeam"; diff --git a/src/store.js b/src/store.js index f99f6b255..26b6434e9 100644 --- a/src/store.js +++ b/src/store.js @@ -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, });