diff --git a/src/course-unit/CourseUnit.jsx b/src/course-unit/CourseUnit.jsx index f94bf3497..7c40f9daf 100644 --- a/src/course-unit/CourseUnit.jsx +++ b/src/course-unit/CourseUnit.jsx @@ -4,11 +4,13 @@ import { useParams } from 'react-router-dom'; import { Container, Layout, Stack } from '@openedx/paragon'; import { useIntl, injectIntl } from '@edx/frontend-platform/i18n'; import { ErrorAlert } from '@edx/frontend-lib-content-components'; +import { Warning as WarningIcon } from '@openedx/paragon/icons'; import { getProcessingNotification } from '../generic/processing-notification/data/selectors'; import SubHeader from '../generic/sub-header/SubHeader'; import { RequestStatus } from '../data/constants'; import getPageHeadTitle from '../generic/utils'; +import AlertMessage from '../generic/alert-message'; import ProcessingNotification from '../generic/processing-notification'; import InternetConnectionAlert from '../generic/internet-connection-alert'; import Loading from '../generic/Loading'; @@ -33,6 +35,7 @@ const CourseUnit = ({ courseId }) => { savingStatus, isTitleEditFormOpen, isErrorAlert, + isLastUnpublishedVersion, isInternetConnectionAlertFailed, unitXBlockActions, handleTitleEditSubmit, @@ -94,6 +97,13 @@ const CourseUnit = ({ courseId }) => { xl={[{ span: 9 }, { span: 3 }]} > + {isLastUnpublishedVersion && ( + + )} {courseVerticalChildren.children.map(({ name, blockId: id, shouldScroll }) => ( ', () => { }); }); + it('should display a warning alert for unpublished course unit version', async () => { + const { getByRole } = render(); + + await waitFor(() => { + const unpublishedAlert = getByRole('alert', { class: 'course-unit-unpublished-alert' }); + expect(unpublishedAlert).toHaveTextContent(messages.alertUnpublishedVersion.defaultMessage); + expect(unpublishedAlert).toHaveClass('alert-warning'); + }); + }); + + it('should not display an unpublished alert for a course unit with explicit staff lock and unpublished status', async () => { + const { queryByRole } = render(); + + axiosMock + .onGet(getCourseUnitApiUrl(courseId)) + .reply(200, { + ...courseUnitIndexMock, + has_explicit_staff_lock: true, + release_date: null, + published: false, + }); + + await executeThunk(fetchCourseUnitQuery(courseId), store.dispatch); + + await waitFor(() => { + const unpublishedAlert = queryByRole('alert', { class: 'course-unit-unpublished-alert' }); + expect(unpublishedAlert).toBeNull(); + }); + }); + it('checks whether xblock is deleted when corresponding delete button is clicked', async () => { axiosMock .onDelete(getXBlockBaseApiUrl(courseVerticalChildrenMock.children[0].block_id)) diff --git a/src/course-unit/hooks.jsx b/src/course-unit/hooks.jsx index b99bfaed1..f6f883828 100644 --- a/src/course-unit/hooks.jsx +++ b/src/course-unit/hooks.jsx @@ -36,6 +36,8 @@ export const useCourseUnit = ({ courseId, blockId }) => { const navigate = useNavigate(); const isTitleEditFormOpen = useSelector(state => state.courseUnit.isTitleEditFormOpen); const isQueryPending = useSelector(state => state.courseUnit.isQueryPending); + const { hasExplicitStaffLock, published, releaseDate } = courseUnit; + const isLastUnpublishedVersion = !hasExplicitStaffLock && published && releaseDate; const unitTitle = courseUnit.metadata?.displayName || ''; const sequenceId = courseUnit.ancestorInfo?.ancestors[0].id; @@ -108,6 +110,7 @@ export const useCourseUnit = ({ courseId, blockId }) => { savingStatus, isQueryPending, isErrorAlert, + isLastUnpublishedVersion, isLoading: loadingStatus.fetchUnitLoadingStatus === RequestStatus.IN_PROGRESS || loadingStatus.courseSectionVerticalLoadingStatus === RequestStatus.IN_PROGRESS, isTitleEditFormOpen, diff --git a/src/course-unit/messages.js b/src/course-unit/messages.js index 42533513e..ba27b0fb7 100644 --- a/src/course-unit/messages.js +++ b/src/course-unit/messages.js @@ -5,6 +5,10 @@ const messages = defineMessages({ id: 'course-authoring.course-unit.general.alert.error.description', defaultMessage: 'Unable to {actionName} {type}. Please try again.', }, + alertUnpublishedVersion: { + id: 'course-authoring.course-unit.general.alert.unpublished-version.description', + defaultMessage: 'Note: The last published version of this unit is live. By publishing changes you will change the student experience.', + }, }); export default messages;