// @ts-check import React, { useState, useEffect } from 'react'; import PropTypes from 'prop-types'; import { useIntl } from '@edx/frontend-platform/i18n'; import { Button, Container, Layout, Row, TransitionReplace, } from '@openedx/paragon'; import { Helmet } from 'react-helmet'; import { Add as IconAdd, CheckCircle as CheckCircleIcon, Warning as WarningIcon, } from '@openedx/paragon/icons'; import { useSelector } from 'react-redux'; import { arrayMove, SortableContext, verticalListSortingStrategy, } from '@dnd-kit/sortable'; import { LoadingSpinner } from '../generic/Loading'; import { getProcessingNotification } from '../generic/processing-notification/data/selectors'; import { RequestStatus } from '../data/constants'; import SubHeader from '../generic/sub-header/SubHeader'; import ProcessingNotification from '../generic/processing-notification'; import InternetConnectionAlert from '../generic/internet-connection-alert'; import DeleteModal from '../generic/delete-modal/DeleteModal'; import AlertMessage from '../generic/alert-message'; import getPageHeadTitle from '../generic/utils'; import { getCurrentItem } from './data/selectors'; import { COURSE_BLOCK_NAMES } from './constants'; import HeaderNavigations from './header-navigations/HeaderNavigations'; import OutlineSideBar from './outline-sidebar/OutlineSidebar'; import StatusBar from './status-bar/StatusBar'; import EnableHighlightsModal from './enable-highlights-modal/EnableHighlightsModal'; import SectionCard from './section-card/SectionCard'; import SubsectionCard from './subsection-card/SubsectionCard'; import UnitCard from './unit-card/UnitCard'; import HighlightsModal from './highlights-modal/HighlightsModal'; import EmptyPlaceholder from './empty-placeholder/EmptyPlaceholder'; import PublishModal from './publish-modal/PublishModal'; import ConfigureModal from './configure-modal/ConfigureModal'; import PageAlerts from './page-alerts/PageAlerts'; import DraggableList from './drag-helper/DraggableList'; import { canMoveSection, possibleUnitMoves, possibleSubsectionMoves, } from './drag-helper/utils'; import { useCourseOutline } from './hooks'; import messages from './messages'; const CourseOutline = ({ courseId }) => { const intl = useIntl(); const { courseName, savingStatus, statusBarData, courseActions, sectionsList, isCustomRelativeDatesActive, isLoading, isReIndexShow, showErrorAlert, showSuccessAlert, isSectionsExpanded, isEnableHighlightsModalOpen, isInternetConnectionAlertFailed, isDisabledReindexButton, isHighlightsModalOpen, isPublishModalOpen, isConfigureModalOpen, isDeleteModalOpen, closeHighlightsModal, closePublishModal, handleConfigureModalClose, closeDeleteModal, openPublishModal, openConfigureModal, openDeleteModal, headerNavigationsActions, openEnableHighlightsModal, closeEnableHighlightsModal, handleEnableHighlightsSubmit, handleInternetConnectionFailed, handleOpenHighlightsModal, handleHighlightsFormSubmit, handleConfigureItemSubmit, handlePublishItemSubmit, handleEditSubmit, handleDeleteItemSubmit, handleDuplicateSectionSubmit, handleDuplicateSubsectionSubmit, handleDuplicateUnitSubmit, handleNewSectionSubmit, handleNewSubsectionSubmit, handleNewUnitSubmit, getUnitUrl, handleVideoSharingOptionChange, handleCopyToClipboardClick, handlePasteClipboardClick, notificationDismissUrl, discussionsSettings, discussionsIncontextFeedbackUrl, discussionsIncontextLearnmoreUrl, deprecatedBlocksInfo, proctoringErrors, mfeProctoredExamSettingsUrl, handleDismissNotification, advanceSettingsUrl, handleSectionDragAndDrop, handleSubsectionDragAndDrop, handleUnitDragAndDrop, } = useCourseOutline({ courseId }); const [sections, setSections] = useState(sectionsList); const restoreSectionList = () => { setSections(() => [...sectionsList]); }; const { isShow: isShowProcessingNotification, title: processingNotificationTitle, } = useSelector(getProcessingNotification); const { category } = useSelector(getCurrentItem); const deleteCategory = COURSE_BLOCK_NAMES[category]?.name.toLowerCase(); /** * Move section to new index * @param {any} currentIndex * @param {any} newIndex */ const updateSectionOrderByIndex = (currentIndex, newIndex) => { if (currentIndex === newIndex) { return; } setSections((prevSections) => { const newSections = arrayMove(prevSections, currentIndex, newIndex); handleSectionDragAndDrop(newSections.map(section => section.id)); return newSections; }); }; /** * Uses details from move information and moves subsection * @param {any} section * @param {any} moveDetails * @returns {void} */ const updateSubsectionOrderByIndex = (section, moveDetails) => { const { fn, args, sectionId } = moveDetails; if (!args) { return; } const [sectionsCopy, newSubsections] = fn(...args); if (newSubsections && sectionId) { setSections(sectionsCopy); handleSubsectionDragAndDrop( sectionId, section.id, newSubsections.map(subsection => subsection.id), restoreSectionList, ); } }; /** * Uses details from move information and moves unit * @param {any} section * @param {any} moveDetails * @returns {void} */ const updateUnitOrderByIndex = (section, moveDetails) => { const { fn, args, sectionId, subsectionId, } = moveDetails; if (!args) { return; } const [sectionsCopy, newUnits] = fn(...args); if (newUnits && sectionId && subsectionId) { setSections(sectionsCopy); handleUnitDragAndDrop( sectionId, section.id, subsectionId, newUnits.map(unit => unit.id), restoreSectionList, ); } }; useEffect(() => { setSections(sectionsList); }, [sectionsList]); if (isLoading) { // eslint-disable-next-line react/jsx-no-useless-fragment return ( ); } return ( <> {getPageHeadTitle(courseName, intl.formatMessage(messages.headingTitle))}
{showSuccessAlert ? ( )} />
{sections.length ? ( <> {sections.map((section, sectionIndex) => ( {section.childInfo.children.map((subsection, subsectionIndex) => ( {subsection.childInfo.children.map((unit, unitIndex) => ( ))} ))} ))} {courseActions.childAddable && ( )} ) : ( )}
{showErrorAlert && (
); }; CourseOutline.propTypes = { courseId: PropTypes.string.isRequired, }; export default CourseOutline;