feat: [FC-0044] Unit page - display xblock components

This commit is contained in:
Ihor Romaniuk
2024-01-23 14:17:15 +01:00
committed by Adolfo R. Brandes
parent e4c3997d17
commit f8095e6670
33 changed files with 1050 additions and 38 deletions

View File

@@ -19,6 +19,7 @@ export const getCourseSectionVerticalApiUrl = (itemId) => `${getStudioBaseUrl()}
export const getLearningSequencesOutlineApiUrl = (courseId) => `${getLmsBaseUrl()}/api/learning_sequences/v1/course_outline/${courseId}`;
export const getCourseMetadataApiUrl = (courseId) => `${getLmsBaseUrl()}/api/courseware/course/${courseId}`;
export const getCourseHomeCourseMetadataApiUrl = (courseId) => `${getLmsBaseUrl()}/api/course_home/course_metadata/${courseId}`;
export const getCourseVerticalChildrenApiUrl = (itemId) => `${getStudioBaseUrl()}/api/contentstore/v1/container/vertical/${itemId}/children`;
export const postXBlockBaseApiUrl = () => `${getStudioBaseUrl()}/xblock/`;
@@ -128,3 +129,43 @@ export async function createCourseXblock({
return data;
}
/**
* Get an object containing course section vertical children data.
* @param {string} itemId
* @returns {Promise<Object>}
*/
export async function getCourseVerticalChildren(itemId) {
const { data } = await getAuthenticatedHttpClient()
.get(getCourseVerticalChildrenApiUrl(itemId));
return camelCaseObject(data);
}
/**
* Delete a unit item.
* @param {string} itemId
* @returns {Promise<Object>}
*/
export async function deleteUnitItem(itemId) {
const { data } = await getAuthenticatedHttpClient()
.delete(getXBlockBaseApiUrl(itemId));
return data;
}
/**
* Duplicate a unit item.
* @param {string} itemId
* @param {string} XBlockId
* @returns {Promise<Object>}
*/
export async function duplicateUnitItem(itemId, XBlockId) {
const { data } = await getAuthenticatedHttpClient()
.post(postXBlockBaseApiUrl(), {
parent_locator: itemId,
duplicate_source_locator: XBlockId,
});
return data;
}

View File

@@ -16,6 +16,7 @@ export const getCoursewareMeta = state => state.models.coursewareMeta;
export const getSections = state => state.models.sections;
export const getCourseId = state => state.courseDetail.courseId;
export const getSequenceId = state => state.courseUnit.sequenceId;
export const getCourseVerticalChildren = state => state.courseUnit.courseVerticalChildren;
export const sequenceIdsSelector = createSelector(
[getCourseStatus, getCoursewareMeta, getSections, getCourseId],
(courseStatus, coursewareMeta, sections, courseId) => {

View File

@@ -12,9 +12,11 @@ const slice = createSlice({
loadingStatus: {
fetchUnitLoadingStatus: RequestStatus.IN_PROGRESS,
courseSectionVerticalLoadingStatus: RequestStatus.IN_PROGRESS,
courseVerticalChildrenLoadingStatus: RequestStatus.IN_PROGRESS,
},
unit: {},
courseSectionVertical: {},
courseVerticalChildren: [],
},
reducers: {
fetchCourseItemSuccess: (state, { payload }) => {
@@ -87,6 +89,28 @@ const slice = createSlice({
fetchUnitLoadingStatus: payload.status,
};
},
updateCourseVerticalChildren: (state, { payload }) => {
state.courseVerticalChildren = payload;
},
updateCourseVerticalChildrenLoadingStatus: (state, { payload }) => {
state.loadingStatus.courseVerticalChildrenLoadingStatus = payload.status;
},
deleteXBlock: (state, { payload }) => {
state.courseVerticalChildren.children = state.courseVerticalChildren.children.filter(
(component) => component.blockId !== payload,
);
},
duplicateXBlock: (state, { payload }) => {
state.courseVerticalChildren = {
...payload.newCourseVerticalChildren,
children: payload.newCourseVerticalChildren.children.map((component) => {
if (component.blockId === payload.newId) {
component.shouldScroll = true;
}
return component;
}),
};
},
},
});
@@ -107,6 +131,10 @@ export const {
changeEditTitleFormOpen,
updateQueryPendingStatus,
updateLoadingCourseXblockStatus,
updateCourseVerticalChildren,
updateCourseVerticalChildrenLoadingStatus,
deleteXBlock,
duplicateXBlock,
} = slice.actions;
export const {

View File

@@ -17,6 +17,9 @@ import {
getCourseHomeCourseMetadata,
getCourseSectionVerticalData,
createCourseXblock,
getCourseVerticalChildren,
deleteUnitItem,
duplicateUnitItem,
} from './api';
import {
updateLoadingCourseUnitStatus,
@@ -32,6 +35,10 @@ import {
fetchCourseSectionVerticalDataSuccess,
updateLoadingCourseSectionVerticalDataStatus,
updateLoadingCourseXblockStatus,
updateCourseVerticalChildren,
updateCourseVerticalChildrenLoadingStatus,
deleteXBlock,
duplicateXBlock,
} from './slice';
export function fetchCourseUnitQuery(courseId) {
@@ -192,7 +199,7 @@ export function fetchCourse(courseId) {
};
}
export function createNewCourseXblock(body, callback) {
export function createNewCourseXBlock(body, callback, blockId) {
return async (dispatch) => {
dispatch(updateLoadingCourseXblockStatus({ status: RequestStatus.IN_PROGRESS }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.adding));
@@ -205,7 +212,8 @@ export function createNewCourseXblock(body, callback) {
const courseSectionVerticalData = await getCourseSectionVerticalData(result.locator);
dispatch(fetchCourseSectionVerticalDataSuccess(courseSectionVerticalData));
}
// ToDo: implement fetching (update) xblocks after success creating
const courseVerticalChildrenData = await getCourseVerticalChildren(blockId);
dispatch(updateCourseVerticalChildren(courseVerticalChildrenData));
dispatch(hideProcessingNotification());
dispatch(updateLoadingCourseXblockStatus({ status: RequestStatus.SUCCESSFUL }));
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
@@ -221,3 +229,55 @@ export function createNewCourseXblock(body, callback) {
}
};
}
export function fetchCourseVerticalChildrenData(itemId) {
return async (dispatch) => {
dispatch(updateCourseVerticalChildrenLoadingStatus({ status: RequestStatus.IN_PROGRESS }));
try {
const courseVerticalChildrenData = await getCourseVerticalChildren(itemId);
dispatch(updateCourseVerticalChildren(courseVerticalChildrenData));
dispatch(updateCourseVerticalChildrenLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
} catch (error) {
dispatch(updateCourseVerticalChildrenLoadingStatus({ status: RequestStatus.FAILED }));
}
};
}
export function deleteUnitItemQuery(itemId, xblockId) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.deleting));
try {
await deleteUnitItem(xblockId);
dispatch(deleteXBlock(xblockId));
dispatch(hideProcessingNotification());
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
} catch (error) {
dispatch(hideProcessingNotification());
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
}
};
}
export function duplicateUnitItemQuery(itemId, xblockId) {
return async (dispatch) => {
dispatch(updateSavingStatus({ status: RequestStatus.PENDING }));
dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.duplicating));
try {
const { locator } = await duplicateUnitItem(itemId, xblockId);
const newCourseVerticalChildren = await getCourseVerticalChildren(itemId);
dispatch(duplicateXBlock({
newId: locator,
newCourseVerticalChildren,
}));
dispatch(hideProcessingNotification());
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
} catch (error) {
dispatch(hideProcessingNotification());
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));
}
};
}