diff --git a/src/data/api.js b/src/data/api.js index 159e8bc7..9998bf46 100644 --- a/src/data/api.js +++ b/src/data/api.js @@ -3,6 +3,9 @@ import { getConfig, camelCaseObject } from '@edx/frontend-platform'; import { getAuthenticatedHttpClient, getAuthenticatedUser } from '@edx/frontend-platform/auth'; import { logError } from '@edx/frontend-platform/logging'; +// FIXME: remove this WIP hack once we're done developing the tab +import datesTabData from './dates'; + function overrideTabUrls(id, tabs) { // "LMS tab slug" to "MFE URL slug" for overridden tabs const tabOverrides = {}; @@ -49,6 +52,16 @@ export async function getCourseMetadata(courseId) { return normalizeMetadata(data); } +export async function getTabData(courseId, tab, version) { + if (tab === 'dates') { + return camelCaseObject(datesTabData()); + } + + const url = `${getConfig().LMS_BASE_URL}/course/${courseId}/api/course_home/${version}/${tab}`; + const { data } = await getAuthenticatedHttpClient().get(url); + return camelCaseObject(data); +} + function normalizeBlocks(courseId, blocks) { const models = { courses: {}, diff --git a/src/data/dates.js b/src/data/dates.js new file mode 100644 index 00000000..618d57b9 --- /dev/null +++ b/src/data/dates.js @@ -0,0 +1,101 @@ +// Sample WIP data while we develop the dates tab +export default function datesData() { + return JSON.parse(` + { + "id": "course-v1:edX+DemoX+Demo_Course", + "isStaff": true, + "number": "DemoX", + "org": "edX", + "title": "Demonstration Course", + "tabs": [ + { + "url": "/courses/course-v1:edX+DemoX+Demo_Course/course/", + "title": "Course", + "slug": "courseware", + "type": "courseware", + "priority": 0 + }, + { + "url": "/courses/course-v1:edX+DemoX+Demo_Course/discussion/forum/", + "title": "Discussion", + "slug": "discussion", + "type": "discussion", + "priority": 1 + }, + { + "url": "/courses/course-v1:edX+DemoX+Demo_Course/course_wiki", + "title": "Wiki", + "slug": "wiki", + "type": "wiki", + "priority": 2 + }, + { + "url": "/courses/course-v1:edX+DemoX+Demo_Course/progress", + "title": "Progress", + "slug": "progress", + "type": "progress", + "priority": 3 + }, + { + "url": "/courses/course-v1:edX+DemoX+Demo_Course/dates", + "title": "Dates", + "slug": "dates", + "type": "dates", + "priority": 4 + }, + { + "url": "/courses/course-v1:edX+DemoX+Demo_Course/instructor", + "title": "Instructor", + "slug": "instructor", + "type": "instructor", + "priority": 5 + } + ], +"dates": [ + { + "title": "Course Starts", + "link": "/testing", + "date": "2015-02-05T05:00:00Z" + }, + { + "contains_gated_content": true, + "link": "/testing", + "title": "Homework - Question Styles force publish", + "date": "2020-01-14T13:00:00Z" + }, + { + "title": "New Subsection", + "date": "2020-04-20T08:30:00Z" + }, + { + "title": "current_datetime", + "date": "2020-05-05T17:47:55.957725Z" + }, + { + "title": "Verification Upgrade Deadline", + "date": "2020-09-16T19:05:00Z" + }, + { + "title": "edX Exams", + "date": "2022-01-02T00:00:00Z" + }, + { + "title": "Homework - Labs and Demos", + "date": "2022-01-14T13:00:00Z" + }, + { + "title": "Homework - Essays", + "date": "2022-04-30T12:00:00Z" + }, + { + "title": "Course End", + "date": "2025-02-09T00:30:00Z" + }, + { + "title": "Verification Deadline", + "date": "2025-02-09T00:30:00Z" + } +] +} +`); +} diff --git a/src/data/index.js b/src/data/index.js index 94799e4f..9317a00b 100644 --- a/src/data/index.js +++ b/src/data/index.js @@ -1,5 +1,6 @@ export { fetchCourse, + fetchDatesTab, fetchSequence, } from './thunks'; diff --git a/src/data/slice.js b/src/data/slice.js index 088e9519..0a40228d 100644 --- a/src/data/slice.js +++ b/src/data/slice.js @@ -43,6 +43,18 @@ const slice = createSlice({ state.sequenceId = payload.sequenceId; state.sequenceStatus = FAILED; }, + fetchTabRequest: (state, { payload }) => { + state.courseId = payload.courseId; + state.courseStatus = LOADING; + }, + fetchTabSuccess: (state, { payload }) => { + state.courseId = payload.courseId; + state.courseStatus = LOADED; + }, + fetchTabFailure: (state, { payload }) => { + state.courseId = payload.courseId; + state.courseStatus = FAILED; + }, }, }); @@ -54,6 +66,9 @@ export const { fetchSequenceRequest, fetchSequenceSuccess, fetchSequenceFailure, + fetchTabRequest, + fetchTabSuccess, + fetchTabFailure, } = slice.actions; export const { diff --git a/src/data/thunks.js b/src/data/thunks.js index 134ebc5a..e14b2aec 100644 --- a/src/data/thunks.js +++ b/src/data/thunks.js @@ -3,6 +3,7 @@ import { getCourseMetadata, getCourseBlocks, getSequenceMetadata, + getTabData, } from './api'; import { addModelsMap, updateModel, updateModels, updateModelsMap, addModel, @@ -15,6 +16,9 @@ import { fetchSequenceRequest, fetchSequenceSuccess, fetchSequenceFailure, + fetchTabRequest, + fetchTabSuccess, + fetchTabFailure, } from './slice'; export function fetchCourse(courseId) { @@ -29,6 +33,17 @@ export function fetchCourse(courseId) { modelType: 'courses', model: courseMetadataResult.value, })); + dispatch(addModel({ + modelType: 'pageInfo', + model: { + id: courseMetadataResult.value.id, + isStaff: courseMetadataResult.value.isStaff, + number: courseMetadataResult.value.number, + org: courseMetadataResult.value.org, + tabs: courseMetadataResult.value.tabs, + title: courseMetadataResult.value.title, + }, + })); } if (courseBlocksResult.status === 'fulfilled') { @@ -85,6 +100,40 @@ export function fetchCourse(courseId) { }; } +export function fetchTab(courseId, tab, version) { + return async (dispatch) => { + dispatch(fetchTabRequest({ courseId })); + getTabData(courseId, tab, version).then((result) => { + dispatch(addModel({ + modelType: 'pageInfo', + model: { + id: result.id, + isStaff: result.isStaff, + number: result.number, + org: result.org, + tabs: result.tabs, + title: result.title, + }, + })); + + dispatch(addModel({ + modelType: tab, + model: result, + })); + + // TODO: do we need access restrictions for tabs, like we have for courseware? + dispatch(fetchTabSuccess({ courseId })); + }, (reason) => { + logError(reason); + dispatch(fetchTabFailure({ courseId })); + }); + }; +} + +export function fetchDatesTab(courseId) { + return fetchTab(courseId, 'dates', 'v1'); +} + export function fetchSequence(sequenceId) { return async (dispatch) => { dispatch(fetchSequenceRequest({ sequenceId })); diff --git a/src/dates-tab/DatesTab.jsx b/src/dates-tab/DatesTab.jsx new file mode 100644 index 00000000..6866659e --- /dev/null +++ b/src/dates-tab/DatesTab.jsx @@ -0,0 +1,12 @@ +import React from 'react'; + +import Timeline from './Timeline'; + +export default function DatesTab() { + return ( + <> +