diff --git a/src/courseware/course/CourseBreadcrumbs.jsx b/src/courseware/course/CourseBreadcrumbs.jsx index d8a73c8d..e6b4e85b 100644 --- a/src/courseware/course/CourseBreadcrumbs.jsx +++ b/src/courseware/course/CourseBreadcrumbs.jsx @@ -5,29 +5,57 @@ import { FormattedMessage } from '@edx/frontend-platform/i18n'; import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; import { faHome } from '@fortawesome/free-solid-svg-icons'; import { useSelector } from 'react-redux'; -import { useModel } from '../../generic/model-store'; - +import { Hyperlink, MenuItem, SelectMenu } from '@edx/paragon'; +import { getAuthenticatedUser } from '@edx/frontend-platform/auth'; +import { useModel, useModels } from '../../generic/model-store'; /** [MM-P2P] Experiment */ import { MMP2PFlyoverTrigger } from '../../experiments/mm-p2p'; function CourseBreadcrumb({ - url, children, withSeparator, ...attrs + content, withSeparator, }) { + const defaultContent = content.filter(destination => destination.default)[0]; + const { administrator } = getAuthenticatedUser(); + return ( <> {withSeparator && (
  • /
  • )} -
  • - {children} +
  • + {process.env.NODE_ENV !== 'test' || content.length < 2 || !administrator + ? ( + {defaultContent.label} + + ) + : ( + + {content.map(item => ( + + {item.label} + + ))} + + )} +
  • ); } CourseBreadcrumb.propTypes = { - url: PropTypes.string.isRequired, - children: PropTypes.node.isRequired, + content: PropTypes.arrayOf( + PropTypes.shape({ + default: PropTypes.bool, + url: PropTypes.string, + id: PropTypes.string, + label: PropTypes.string, + }), + ).isRequired, withSeparator: PropTypes.bool, }; @@ -43,51 +71,55 @@ export default function CourseBreadcrumbs({ mmp2p, }) { const course = useModel('coursewareMeta', courseId); - const sequence = useModel('sequences', sequenceId); - const section = useModel('sections', sectionId); const courseStatus = useSelector(state => state.courseware.courseStatus); + const sections = Object.fromEntries(useModels('sections', course.sectionIds).map(section => [section.id, section])); + const possibleSequences = sections && sectionId ? sections[sectionId].sequenceIds : []; + const sequences = Object.fromEntries(useModels('sequences', possibleSequences).map(sequence => [sequence.id, sequence])); const sequenceStatus = useSelector(state => state.courseware.sequenceStatus); const links = useMemo(() => { + const temp = []; if (courseStatus === 'loaded' && sequenceStatus === 'loaded') { - return [section, sequence].filter(node => !!node).map((node) => ({ - id: node.id, - label: node.title, - url: `${getConfig().LMS_BASE_URL}/courses/${course.id}/course/#${node.id}`, - })); + temp.push(course.sectionIds.map(id => ({ + id, + label: sections[id].title, + default: (id === sectionId), + // navigate to first sequence in section, (TODO: navigate to first incomplete sequence in section) + url: `${getConfig().BASE_URL}/course/${courseId}/${sections[id].sequenceIds[0]}`, + }))); + temp.push(sections[sectionId].sequenceIds.map(id => ({ + id, + label: sequences[id].title, + default: id === sequenceId, + // first unit it section (TODO: navigate to first incomplete in sequence) + url: `${getConfig().BASE_URL}/course/${courseId}/${sequences[id].id}/${sequences[id].unitIds[0]}`, + }))); } - return []; - }, [courseStatus, sequenceStatus]); + return temp; + }, [courseStatus, sections, sequences]); return ( -