diff --git a/src/course-home/CourseHome.jsx b/src/course-home/CourseHome.jsx
index 877fc839..c26351c8 100644
--- a/src/course-home/CourseHome.jsx
+++ b/src/course-home/CourseHome.jsx
@@ -5,11 +5,12 @@ import { Button } from '@edx/paragon';
import { AlertList } from '../user-messages';
import CourseDates from './CourseDates';
+import CourseTools from './CourseTools';
import Section from './Section';
import { useModel } from '../model-store';
// Note that we import from the component files themselves in the enrollment-alert package.
-// This is because Reacy.lazy() requires that we import() from a file with a Component as it's
+// This is because React.lazy() requires that we import() from a file with a Component as its
// default export.
// See React.lazy docs here: https://reactjs.org/docs/code-splitting.html#reactlazy
const { EnrollmentAlert, StaffEnrollmentAlert } = React.lazy(() => import('../enrollment-alert'));
@@ -57,6 +58,9 @@ export default function CourseHome() {
))}
+
{
+ switch (iconClasses) {
+ case 'edx.bookmarks':
+ return faBookmark;
+ case 'edx.tool.verified_upgrade':
+ return faCertificate;
+ case 'edx.tool.financial_assistance':
+ return faInfo;
+ case 'edx.calendar-sync':
+ return faCalendar;
+ case 'edx.updates':
+ return faNewspaper;
+ case 'edx.reviews':
+ return faStar;
+ default:
+ return null;
+ }
+ };
+
+ return (
+
+ Course Tools
+ {courseTools.map((courseTool) => (
+
+ ))}
+
+ );
+}
+
+CourseTools.propTypes = {
+ courseId: PropTypes.string,
+};
+
+CourseTools.defaultProps = {
+ courseId: null,
+};
diff --git a/src/data/api.js b/src/data/api.js
index c8bffa78..9653cafa 100644
--- a/src/data/api.js
+++ b/src/data/api.js
@@ -63,6 +63,17 @@ export async function getTabData(courseId, tab, version) {
}
}
+function normalizeOutlineTabData(courseId, courseToolData) {
+ const courseTools = camelCaseObject(courseToolData);
+ return { id: courseId, courseTools };
+}
+
+export async function getOutlineTabData(courseId) {
+ const url = `${getConfig().LMS_BASE_URL}/api/course_home/v1/outline/${courseId}`;
+ const { data } = await getAuthenticatedHttpClient().get(url, {});
+ return normalizeOutlineTabData(courseId, data.course_tools);
+}
+
function normalizeBlocks(courseId, blocks) {
const models = {
courses: {},
diff --git a/src/data/thunks.js b/src/data/thunks.js
index 986352c8..604d4cbd 100644
--- a/src/data/thunks.js
+++ b/src/data/thunks.js
@@ -4,6 +4,7 @@ import {
getCourseBlocks,
getSequenceMetadata,
getTabData,
+ getOutlineTabData,
} from './api';
import {
addModelsMap, updateModel, updateModels, updateModelsMap, addModel,
@@ -27,7 +28,8 @@ export function fetchCourse(courseId) {
Promise.allSettled([
getCourseMetadata(courseId),
getCourseBlocks(courseId),
- ]).then(([courseMetadataResult, courseBlocksResult]) => {
+ getOutlineTabData(courseId),
+ ]).then(([courseMetadataResult, courseBlocksResult, outlineTabResult]) => {
if (courseMetadataResult.status === 'fulfilled') {
dispatch(addModel({
modelType: 'courses',
@@ -70,8 +72,16 @@ export function fetchCourse(courseId) {
}));
}
+ if (outlineTabResult.status === 'fulfilled') {
+ dispatch(addModel({
+ modelType: 'outline',
+ model: outlineTabResult.value,
+ }));
+ }
+
const fetchedMetadata = courseMetadataResult.status === 'fulfilled';
const fetchedBlocks = courseBlocksResult.status === 'fulfilled';
+ const fetchedOutline = outlineTabResult.status === 'fulfilled';
// Log errors for each request if needed. Course block failures may occur
// even if the course metadata request is successful
@@ -81,15 +91,18 @@ export function fetchCourse(courseId) {
if (!fetchedMetadata) {
logError(courseMetadataResult.reason);
}
+ if (!fetchedOutline) {
+ logError(outlineTabResult.reason);
+ }
if (fetchedMetadata) {
- if (courseMetadataResult.value.canLoadCourseware.hasAccess && fetchedBlocks) {
+ if (courseMetadataResult.value.canLoadCourseware.hasAccess && fetchedBlocks && fetchedOutline) {
// User has access
dispatch(fetchCourseSuccess({ courseId }));
return;
}
// User either doesn't have access or only has partial access
- // (can't access course blocks)
+ // (can't access course blocks or course outline)
dispatch(fetchCourseDenied({ courseId }));
return;
}