From 7d4f53644c73433dfd7f7d983316ccceafe35868 Mon Sep 17 00:00:00 2001 From: Sid Verma Date: Tue, 16 Jan 2024 20:28:19 +0530 Subject: [PATCH 1/2] feat: Add drag-n-drop support to course unit, refactor tests. --- src/course-outline/CourseOutline.jsx | 64 ++++++--- src/course-outline/CourseOutline.test.jsx | 164 ++++++++++++++-------- src/course-outline/data/api.js | 22 +++ src/course-outline/data/slice.js | 10 ++ src/course-outline/data/thunk.js | 23 +++ src/course-outline/hooks.jsx | 6 + src/course-outline/unit-card/UnitCard.jsx | 4 +- 7 files changed, 220 insertions(+), 73 deletions(-) diff --git a/src/course-outline/CourseOutline.jsx b/src/course-outline/CourseOutline.jsx index fe9baf17e8..2cd849d001 100644 --- a/src/course-outline/CourseOutline.jsx +++ b/src/course-outline/CourseOutline.jsx @@ -94,6 +94,7 @@ const CourseOutline = ({ courseId }) => { handleSectionDragAndDrop, handleSubsectionDragAndDrop, handleVideoSharingOptionChange, + handleUnitDragAndDrop, } = useCourseOutline({ courseId }); const [sections, setSections] = useState(sectionsList); @@ -126,6 +127,27 @@ const CourseOutline = ({ courseId }) => { }); }; + const setUnit = (sectionIndex, subsectionIndex) => (updatedUnits) => { + const section = { ...sections[sectionIndex] }; + section.childInfo = { ...section.childInfo }; + + const subsection = { ...section.childInfo.children[subsectionIndex] }; + subsection.childInfo = { ...subsection.childInfo }; + subsection.childInfo.children = updatedUnits(); + + const updatedSubsection = [...subsection.childInfo.children]; + updatedSubsection[subsectionIndex] = subsection; + section.childInfo.children = updatedSubsection; + setSections([...sections.slice(0, sectionIndex), section, ...sections.slice(sectionIndex + 1)]); + }; + + const finalizeUnitOrder = (section, subsection) => () => (newUnits) => { + initialSections = [...sectionsList]; + handleUnitDragAndDrop(section.id, subsection.id, newUnits.map(unit => unit.id), () => { + setSections(() => initialSections); + }); + }; + useEffect(() => { setSections(sectionsList); }, [sectionsList]); @@ -201,7 +223,7 @@ const CourseOutline = ({ courseId }) => { {sections.length ? ( <> - {sections.map((section, index) => ( + {sections.map((section, sectionIndex) => ( { > - {section.childInfo.children.map((subsection) => ( + {section.childInfo.children.map((subsection, subsectionIndex) => ( { onDuplicateSubmit={handleDuplicateSubsectionSubmit} onNewUnitSubmit={handleNewUnitSubmit} > - {subsection.childInfo.children.map((unit) => ( - - ))} + {subsection.childInfo && ( + + {subsection.childInfo.children.map((unit) => ( + + ))} + + )} ))} diff --git a/src/course-outline/CourseOutline.test.jsx b/src/course-outline/CourseOutline.test.jsx index 178cb3fa60..92446c6908 100644 --- a/src/course-outline/CourseOutline.test.jsx +++ b/src/course-outline/CourseOutline.test.jsx @@ -18,6 +18,7 @@ import { getCourseItemApiUrl, getXBlockBaseApiUrl, getChapterBlockApiUrl, + getSequentialBlockApiUrl, } from './data/api'; import { RequestStatus } from '../data/constants'; import { @@ -688,108 +689,161 @@ describe('', () => { }); it('check that new section list is saved when dragged', async () => { - const { getAllByRole } = render(); - + const { findAllByRole } = render(); const courseBlockId = courseOutlineIndexMock.courseStructure.id; - await waitFor(async () => { - const sectionsDraggers = await getAllByRole('button', { name: 'Drag to reorder' }); + const sectionsDraggers = await findAllByRole('button', { name: 'Drag to reorder' }); + const draggableButton = sectionsDraggers[7]; - axiosMock - .onPut(getCourseBlockApiUrl(courseBlockId)) - .reply(200, { dummy: 'value' }); + axiosMock + .onPut(getCourseBlockApiUrl(courseBlockId)) + .reply(200, { dummy: 'value' }); + + const section1 = store.getState().courseOutline.sectionsList[0].id; - const section1 = store.getState().courseOutline.sectionsList[0].id; - const draggableButton = sectionsDraggers[7]; + fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); + await waitFor(async () => { fireEvent.keyDown(draggableButton, { code: 'Space' }); - fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); - await act(async () => fireEvent.keyDown(draggableButton, { code: 'Space' })); const saveStatus = store.getState().courseOutline.savingStatus; expect(saveStatus).toEqual(RequestStatus.SUCCESSFUL); - - const section2 = store.getState().courseOutline.sectionsList[1].id; - expect(section1).toBe(section2); }); + + const section2 = store.getState().courseOutline.sectionsList[1].id; + expect(section1).toBe(section2); }); it('check section list is restored to original order when API call fails', async () => { - const { getAllByRole } = render(); - + const { findAllByRole } = render(); const courseBlockId = courseOutlineIndexMock.courseStructure.id; - await waitFor(async () => { - const sectionsDraggers = await getAllByRole('button', { name: 'Drag to reorder' }); + const sectionsDraggers = await findAllByRole('button', { name: 'Drag to reorder' }); + const draggableButton = sectionsDraggers[6]; - axiosMock - .onPut(getCourseBlockApiUrl(courseBlockId)) - .reply(500); + axiosMock + .onPut(getCourseBlockApiUrl(courseBlockId)) + .reply(500); + + const section1 = store.getState().courseOutline.sectionsList[0].id; - const section1 = store.getState().courseOutline.sectionsList[0].id; - const draggableButton = sectionsDraggers[6]; + fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); + await waitFor(async () => { fireEvent.keyDown(draggableButton, { code: 'Space' }); - fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); - await act(async () => fireEvent.keyDown(draggableButton, { code: 'Space' })); const saveStatus = store.getState().courseOutline.savingStatus; expect(saveStatus).toEqual(RequestStatus.FAILED); - - const section1New = store.getState().courseOutline.sectionsList[0].id; - expect(section1).toBe(section1New); }); + + const section1New = store.getState().courseOutline.sectionsList[0].id; + expect(section1).toBe(section1New); }); it('check that new subsection list is saved when dragged', async () => { const { findAllByTestId } = render(); const courseBlockId = courseOutlineIndexMock.courseStructure.id; - await waitFor(async () => { - const [section] = await findAllByTestId('section-card'); - const subsectionsDraggers = within(section).getAllByRole('button', { name: 'Drag to reorder' }); + const [sectionElement] = await findAllByTestId('section-card'); + const section = store.getState().courseOutline.sectionsList[0]; + const subsectionsDraggers = within(sectionElement).getAllByRole('button', { name: 'Drag to reorder' }); + const draggableButton = subsectionsDraggers[1]; - axiosMock - .onPut(getChapterBlockApiUrl(courseBlockId, store.getState().courseOutline.sectionsList[0].id)) - .reply(200, { dummy: 'value' }); + axiosMock + .onPut(getChapterBlockApiUrl(courseBlockId, section.id)) + .reply(200, { dummy: 'value' }); - const subsection1 = store.getState().courseOutline.sectionsList[0].childInfo.children[0].id; + const subsection1 = section.childInfo.children[0].id; - // Move the second subsection up - const draggableButton = subsectionsDraggers[1]; + fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); + await waitFor(async () => { fireEvent.keyDown(draggableButton, { code: 'Space' }); - fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); - await act(async () => fireEvent.keyDown(draggableButton, { code: 'Space' })); + const saveStatus = store.getState().courseOutline.savingStatus; expect(saveStatus).toEqual(RequestStatus.SUCCESSFUL); - - const subsection2 = store.getState().courseOutline.sectionsList[0].childInfo.children[1].id; - expect(subsection1).toBe(subsection2); }); + + const subsection2 = store.getState().courseOutline.sectionsList[0].childInfo.children[1].id; + expect(subsection1).toBe(subsection2); }); it('check that new subsection list is restored to original order when API call fails', async () => { const { findAllByTestId } = render(); const courseBlockId = courseOutlineIndexMock.courseStructure.id; - await waitFor(async () => { - const [section] = await findAllByTestId('section-card'); - const subsectionsDraggers = within(section).getAllByRole('button', { name: 'Drag to reorder' }); + const [sectionElement] = await findAllByTestId('section-card'); + const section = store.getState().courseOutline.sectionsList[0]; + const subsectionsDraggers = within(sectionElement).getAllByRole('button', { name: 'Drag to reorder' }); + const draggableButton = subsectionsDraggers[1]; - axiosMock - .onPut(getChapterBlockApiUrl(courseBlockId, store.getState().courseOutline.sectionsList[0].id)) - .reply(500); + axiosMock + .onPut(getChapterBlockApiUrl(courseBlockId, section.id)) + .reply(500); - const subsection1 = store.getState().courseOutline.sectionsList[0].childInfo.children[0].id; + const subsection1 = section.childInfo.children[0].id; - // Move the second subsection up - const draggableButton = subsectionsDraggers[1]; + fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); + await waitFor(async () => { fireEvent.keyDown(draggableButton, { code: 'Space' }); - fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); - await act(async () => fireEvent.keyDown(draggableButton, { code: 'Space' })); const saveStatus = store.getState().courseOutline.savingStatus; expect(saveStatus).toEqual(RequestStatus.FAILED); + }); + + const subsection1New = store.getState().courseOutline.sectionsList[0].childInfo.children[0].id; + expect(subsection1).toBe(subsection1New); + }); + + it('check that new unit list is saved when dragged', async () => { + const { findAllByTestId } = render(); + const courseBlockId = courseOutlineIndexMock.courseStructure.id; + const subsectionElement = (await findAllByTestId('subsection-card'))[3]; + const subsection = store.getState().courseOutline.sectionsList[1].childInfo.children[0]; + const expandBtn = within(subsectionElement).getByTestId('subsection-card-header__expanded-btn'); + fireEvent.click(expandBtn); + const unitDraggers = await within(subsectionElement).findAllByRole('button', { name: 'Drag to reorder' }); + const draggableButton = unitDraggers[1]; + + axiosMock + .onPut(getSequentialBlockApiUrl(courseBlockId, subsection.id)) + .reply(200, { dummy: 'value' }); + + const unit1 = subsection.childInfo.children[0].id; + + fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); + await waitFor(async () => { + fireEvent.keyDown(draggableButton, { code: 'Space' }); + + const saveStatus = store.getState().courseOutline.savingStatus; + expect(saveStatus).toEqual(RequestStatus.SUCCESSFUL); + }); + + const unit2 = store.getState().courseOutline.sectionsList[1].childInfo.children[0].childInfo.children[1].id; + expect(unit1).toBe(unit2); + }); - const subsection1New = store.getState().courseOutline.sectionsList[0].childInfo.children[0].id; - expect(subsection1).toBe(subsection1New); + it('check that new unit list is restored to original order when API call fails', async () => { + const { findAllByTestId } = render(); + const courseBlockId = courseOutlineIndexMock.courseStructure.id; + const subsectionElement = (await findAllByTestId('subsection-card'))[3]; + const subsection = store.getState().courseOutline.sectionsList[1].childInfo.children[0]; + const expandBtn = within(subsectionElement).getByTestId('subsection-card-header__expanded-btn'); + fireEvent.click(expandBtn); + const unitDraggers = await within(subsectionElement).findAllByRole('button', { name: 'Drag to reorder' }); + const draggableButton = unitDraggers[1]; + + axiosMock + .onPut(getSequentialBlockApiUrl(courseBlockId, subsection.id)) + .reply(500); + + const unit1 = subsection.childInfo.children[0].id; + + fireEvent.keyDown(draggableButton, { key: 'ArrowUp' }); + await waitFor(async () => { + fireEvent.keyDown(draggableButton, { code: 'Space' }); + + const saveStatus = store.getState().courseOutline.savingStatus; + expect(saveStatus).toEqual(RequestStatus.FAILED); }); + + const unit1New = store.getState().courseOutline.sectionsList[1].childInfo.children[0].childInfo.children[0].id; + expect(unit1).toBe(unit1New); }); it('check that drag handle is not visible for non-draggable sections', async () => { diff --git a/src/course-outline/data/api.js b/src/course-outline/data/api.js index 8f40f023f1..673034a0d3 100644 --- a/src/course-outline/data/api.js +++ b/src/course-outline/data/api.js @@ -30,6 +30,12 @@ export const getChapterBlockApiUrl = (courseId, chapterId) => { return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@chapter+block@${formattedChapterId}`; }; +export const getSequentialBlockApiUrl = (courseId, unitId) => { + const formattedCourseId = courseId.split('course-v1:')[1]; + const formattedUnitId = unitId.split('@').slice(-1)[0]; + return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@sequential+block@${formattedUnitId}`; +}; + export const getCourseReindexApiUrl = (reindexLink) => `${getApiBaseUrl()}${reindexLink}`; export const getXBlockBaseApiUrl = () => `${getApiBaseUrl()}/xblock/`; export const getCourseItemApiUrl = (itemId) => `${getXBlockBaseApiUrl()}${itemId}`; @@ -350,3 +356,19 @@ export async function setVideoSharingOption(courseId, videoSharingOption) { return data; } + +/** + * Set order for the list of the units + * @param {string} courseId + * @param {string} subsectionId + * @param {Array} children list of unit id's + * @returns {Promise} +*/ +export async function setUnitOrderList(courseId, subsectionId, children) { + const { data } = await getAuthenticatedHttpClient() + .put(getSequentialBlockApiUrl(courseId, subsectionId), { + children, + }); + + return data; +} diff --git a/src/course-outline/data/slice.js b/src/course-outline/data/slice.js index dca7734682..33f7344f02 100644 --- a/src/course-outline/data/slice.js +++ b/src/course-outline/data/slice.js @@ -104,6 +104,15 @@ const slice = createSlice({ sections[i].childInfo.children.sort((a, b) => subsectionListIds.indexOf(a.id) - subsectionListIds.indexOf(b.id)); state.sectionsList = [...sections]; }, + reorderUnitList: (state, { payload }) => { + const { sectionId, subsectionId, unitListIds } = payload; + const sections = [...state.sectionsList]; + const i = sections.findIndex(section => section.id === sectionId); + const j = sections[i].childInfo.children.findIndex(subsection => subsection.id === subsectionId); + const subsection = sections[i].childInfo.children[j]; + subsection.childInfo.children.sort((a, b) => unitListIds.indexOf(a.id) - unitListIds.indexOf(b.id)); + state.sectionsList = [...sections]; + }, setCurrentSection: (state, { payload }) => { state.currentSection = payload; }, @@ -193,6 +202,7 @@ export const { duplicateSection, reorderSectionList, reorderSubsectionList, + reorderUnitList, } = slice.actions; export const { diff --git a/src/course-outline/data/thunk.js b/src/course-outline/data/thunk.js index 4f7abcbf75..03054477a7 100644 --- a/src/course-outline/data/thunk.js +++ b/src/course-outline/data/thunk.js @@ -26,6 +26,7 @@ import { setSectionOrderList, setVideoSharingOption, setSubsectionOrderList, + setUnitOrderList, } from './api'; import { addSection, @@ -46,6 +47,7 @@ import { duplicateSection, reorderSectionList, reorderSubsectionList, + reorderUnitList, } from './slice'; export function fetchCourseOutlineIndexQuery(courseId) { @@ -488,3 +490,24 @@ export function setSubsectionOrderListQuery(courseId, sectionId, subsectionListI } }; } + +export function setUnitOrderListQuery(courseId, sectionId, subsectionId, unitListIds, restoreCallback) { + return async (dispatch) => { + dispatch(updateSavingStatus({ status: RequestStatus.PENDING })); + dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving)); + + try { + await setUnitOrderList(courseId, subsectionId, unitListIds).then(async (result) => { + if (result) { + dispatch(reorderUnitList({ sectionId, subsectionId, unitListIds })); + dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL })); + dispatch(hideProcessingNotification()); + } + }); + } catch (error) { + restoreCallback(); + dispatch(hideProcessingNotification()); + dispatch(updateSavingStatus({ status: RequestStatus.FAILED })); + } + }; +} diff --git a/src/course-outline/hooks.jsx b/src/course-outline/hooks.jsx index 4d797685c4..9ef7bc9cc4 100644 --- a/src/course-outline/hooks.jsx +++ b/src/course-outline/hooks.jsx @@ -44,6 +44,7 @@ import { setSectionOrderListQuery, setVideoSharingOptionQuery, setSubsectionOrderListQuery, + setUnitOrderListQuery, } from './data/thunk'; const useCourseOutline = ({ courseId }) => { @@ -198,6 +199,10 @@ const useCourseOutline = ({ courseId }) => { dispatch(setVideoSharingOptionQuery(courseId, value)); }; + const handleUnitDragAndDrop = (sectionId, subsectionId, unitListIds, restoreCallback) => { + dispatch(setUnitOrderListQuery(courseId, sectionId, subsectionId, unitListIds, restoreCallback)); + }; + useEffect(() => { dispatch(fetchCourseOutlineIndexQuery(courseId)); dispatch(fetchCourseBestPracticesQuery({ courseId })); @@ -261,6 +266,7 @@ const useCourseOutline = ({ courseId }) => { handleSectionDragAndDrop, handleSubsectionDragAndDrop, handleVideoSharingOptionChange, + handleUnitDragAndDrop, }; }; diff --git a/src/course-outline/unit-card/UnitCard.jsx b/src/course-outline/unit-card/UnitCard.jsx index 7a341eef03..4652dc0da1 100644 --- a/src/course-outline/unit-card/UnitCard.jsx +++ b/src/course-outline/unit-card/UnitCard.jsx @@ -96,7 +96,9 @@ const UnitCard = ({ Date: Wed, 17 Jan 2024 19:51:36 +0530 Subject: [PATCH 2/2] chore: address review feedback --- src/course-outline/CourseOutline.jsx | 48 +++++++++++------------ src/course-outline/CourseOutline.test.jsx | 22 ++++------- src/course-outline/data/api.js | 35 ++--------------- src/course-outline/data/thunk.js | 11 +++--- src/course-outline/hooks.jsx | 4 +- 5 files changed, 41 insertions(+), 79 deletions(-) diff --git a/src/course-outline/CourseOutline.jsx b/src/course-outline/CourseOutline.jsx index 2cd849d001..25cf0a3873 100644 --- a/src/course-outline/CourseOutline.jsx +++ b/src/course-outline/CourseOutline.jsx @@ -135,9 +135,9 @@ const CourseOutline = ({ courseId }) => { subsection.childInfo = { ...subsection.childInfo }; subsection.childInfo.children = updatedUnits(); - const updatedSubsection = [...subsection.childInfo.children]; - updatedSubsection[subsectionIndex] = subsection; - section.childInfo.children = updatedSubsection; + const updatedSubsections = [...section.childInfo.children]; + updatedSubsections[subsectionIndex] = subsection; + section.childInfo.children = updatedSubsections; setSections([...sections.slice(0, sectionIndex), section, ...sections.slice(sectionIndex + 1)]); }; @@ -255,28 +255,26 @@ const CourseOutline = ({ courseId }) => { onDuplicateSubmit={handleDuplicateSubsectionSubmit} onNewUnitSubmit={handleNewUnitSubmit} > - {subsection.childInfo && ( - - {subsection.childInfo.children.map((unit) => ( - - ))} - - )} + + {subsection.childInfo.children.map((unit) => ( + + ))} + ))} diff --git a/src/course-outline/CourseOutline.test.jsx b/src/course-outline/CourseOutline.test.jsx index 92446c6908..e5e1045e98 100644 --- a/src/course-outline/CourseOutline.test.jsx +++ b/src/course-outline/CourseOutline.test.jsx @@ -17,8 +17,6 @@ import { getCourseBlockApiUrl, getCourseItemApiUrl, getXBlockBaseApiUrl, - getChapterBlockApiUrl, - getSequentialBlockApiUrl, } from './data/api'; import { RequestStatus } from '../data/constants'; import { @@ -739,14 +737,13 @@ describe('', () => { it('check that new subsection list is saved when dragged', async () => { const { findAllByTestId } = render(); - const courseBlockId = courseOutlineIndexMock.courseStructure.id; const [sectionElement] = await findAllByTestId('section-card'); - const section = store.getState().courseOutline.sectionsList[0]; + const [section] = store.getState().courseOutline.sectionsList; const subsectionsDraggers = within(sectionElement).getAllByRole('button', { name: 'Drag to reorder' }); const draggableButton = subsectionsDraggers[1]; axiosMock - .onPut(getChapterBlockApiUrl(courseBlockId, section.id)) + .onPut(getCourseItemApiUrl(section.id)) .reply(200, { dummy: 'value' }); const subsection1 = section.childInfo.children[0].id; @@ -766,14 +763,13 @@ describe('', () => { it('check that new subsection list is restored to original order when API call fails', async () => { const { findAllByTestId } = render(); - const courseBlockId = courseOutlineIndexMock.courseStructure.id; const [sectionElement] = await findAllByTestId('section-card'); - const section = store.getState().courseOutline.sectionsList[0]; + const [section] = store.getState().courseOutline.sectionsList; const subsectionsDraggers = within(sectionElement).getAllByRole('button', { name: 'Drag to reorder' }); const draggableButton = subsectionsDraggers[1]; axiosMock - .onPut(getChapterBlockApiUrl(courseBlockId, section.id)) + .onPut(getCourseItemApiUrl(section.id)) .reply(500); const subsection1 = section.childInfo.children[0].id; @@ -792,16 +788,15 @@ describe('', () => { it('check that new unit list is saved when dragged', async () => { const { findAllByTestId } = render(); - const courseBlockId = courseOutlineIndexMock.courseStructure.id; const subsectionElement = (await findAllByTestId('subsection-card'))[3]; - const subsection = store.getState().courseOutline.sectionsList[1].childInfo.children[0]; + const [subsection] = store.getState().courseOutline.sectionsList[1].childInfo.children; const expandBtn = within(subsectionElement).getByTestId('subsection-card-header__expanded-btn'); fireEvent.click(expandBtn); const unitDraggers = await within(subsectionElement).findAllByRole('button', { name: 'Drag to reorder' }); const draggableButton = unitDraggers[1]; axiosMock - .onPut(getSequentialBlockApiUrl(courseBlockId, subsection.id)) + .onPut(getCourseItemApiUrl(subsection.id)) .reply(200, { dummy: 'value' }); const unit1 = subsection.childInfo.children[0].id; @@ -820,16 +815,15 @@ describe('', () => { it('check that new unit list is restored to original order when API call fails', async () => { const { findAllByTestId } = render(); - const courseBlockId = courseOutlineIndexMock.courseStructure.id; const subsectionElement = (await findAllByTestId('subsection-card'))[3]; - const subsection = store.getState().courseOutline.sectionsList[1].childInfo.children[0]; + const [subsection] = store.getState().courseOutline.sectionsList[1].childInfo.children; const expandBtn = within(subsectionElement).getByTestId('subsection-card-header__expanded-btn'); fireEvent.click(expandBtn); const unitDraggers = await within(subsectionElement).findAllByRole('button', { name: 'Drag to reorder' }); const draggableButton = unitDraggers[1]; axiosMock - .onPut(getSequentialBlockApiUrl(courseBlockId, subsection.id)) + .onPut(getCourseItemApiUrl(subsection.id)) .reply(500); const unit1 = subsection.childInfo.children[0].id; diff --git a/src/course-outline/data/api.js b/src/course-outline/data/api.js index 673034a0d3..4cb0835961 100644 --- a/src/course-outline/data/api.js +++ b/src/course-outline/data/api.js @@ -24,18 +24,6 @@ export const getCourseBlockApiUrl = (courseId) => { return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@course+block@course`; }; -export const getChapterBlockApiUrl = (courseId, chapterId) => { - const formattedCourseId = courseId.split('course-v1:')[1]; - const formattedChapterId = chapterId.split('@').slice(-1)[0]; - return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@chapter+block@${formattedChapterId}`; -}; - -export const getSequentialBlockApiUrl = (courseId, unitId) => { - const formattedCourseId = courseId.split('course-v1:')[1]; - const formattedUnitId = unitId.split('@').slice(-1)[0]; - return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@sequential+block@${formattedUnitId}`; -}; - export const getCourseReindexApiUrl = (reindexLink) => `${getApiBaseUrl()}${reindexLink}`; export const getXBlockBaseApiUrl = () => `${getApiBaseUrl()}/xblock/`; export const getCourseItemApiUrl = (itemId) => `${getXBlockBaseApiUrl()}${itemId}`; @@ -326,14 +314,13 @@ export async function setSectionOrderList(courseId, children) { /** * Set order for the list of the subsections - * @param {string} courseId - * @param {string} sectionId + * @param {string} itemId Subsection or unit ID * @param {Array} children list of sections id's * @returns {Promise} */ -export async function setSubsectionOrderList(courseId, sectionId, children) { +export async function setCourseItemOrderList(itemId, children) { const { data } = await getAuthenticatedHttpClient() - .put(getChapterBlockApiUrl(courseId, sectionId), { + .put(getCourseItemApiUrl(itemId), { children, }); @@ -356,19 +343,3 @@ export async function setVideoSharingOption(courseId, videoSharingOption) { return data; } - -/** - * Set order for the list of the units - * @param {string} courseId - * @param {string} subsectionId - * @param {Array} children list of unit id's - * @returns {Promise} -*/ -export async function setUnitOrderList(courseId, subsectionId, children) { - const { data } = await getAuthenticatedHttpClient() - .put(getSequentialBlockApiUrl(courseId, subsectionId), { - children, - }); - - return data; -} diff --git a/src/course-outline/data/thunk.js b/src/course-outline/data/thunk.js index 03054477a7..ca1fe663f4 100644 --- a/src/course-outline/data/thunk.js +++ b/src/course-outline/data/thunk.js @@ -25,8 +25,7 @@ import { updateCourseSectionHighlights, setSectionOrderList, setVideoSharingOption, - setSubsectionOrderList, - setUnitOrderList, + setCourseItemOrderList, } from './api'; import { addSection, @@ -470,13 +469,13 @@ export function setSectionOrderListQuery(courseId, sectionListIds, restoreCallba }; } -export function setSubsectionOrderListQuery(courseId, sectionId, subsectionListIds, restoreCallback) { +export function setSubsectionOrderListQuery(sectionId, subsectionListIds, restoreCallback) { return async (dispatch) => { dispatch(updateSavingStatus({ status: RequestStatus.PENDING })); dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving)); try { - await setSubsectionOrderList(courseId, sectionId, subsectionListIds).then(async (result) => { + await setCourseItemOrderList(sectionId, subsectionListIds).then(async (result) => { if (result) { dispatch(reorderSubsectionList({ sectionId, subsectionListIds })); dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL })); @@ -491,13 +490,13 @@ export function setSubsectionOrderListQuery(courseId, sectionId, subsectionListI }; } -export function setUnitOrderListQuery(courseId, sectionId, subsectionId, unitListIds, restoreCallback) { +export function setUnitOrderListQuery(sectionId, subsectionId, unitListIds, restoreCallback) { return async (dispatch) => { dispatch(updateSavingStatus({ status: RequestStatus.PENDING })); dispatch(showProcessingNotification(NOTIFICATION_MESSAGES.saving)); try { - await setUnitOrderList(courseId, subsectionId, unitListIds).then(async (result) => { + await setCourseItemOrderList(subsectionId, unitListIds).then(async (result) => { if (result) { dispatch(reorderUnitList({ sectionId, subsectionId, unitListIds })); dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL })); diff --git a/src/course-outline/hooks.jsx b/src/course-outline/hooks.jsx index 9ef7bc9cc4..4932cb4af4 100644 --- a/src/course-outline/hooks.jsx +++ b/src/course-outline/hooks.jsx @@ -192,7 +192,7 @@ const useCourseOutline = ({ courseId }) => { }; const handleSubsectionDragAndDrop = (sectionId, subsectionListIds, restoreCallback) => { - dispatch(setSubsectionOrderListQuery(courseId, sectionId, subsectionListIds, restoreCallback)); + dispatch(setSubsectionOrderListQuery(sectionId, subsectionListIds, restoreCallback)); }; const handleVideoSharingOptionChange = (value) => { @@ -200,7 +200,7 @@ const useCourseOutline = ({ courseId }) => { }; const handleUnitDragAndDrop = (sectionId, subsectionId, unitListIds, restoreCallback) => { - dispatch(setUnitOrderListQuery(courseId, sectionId, subsectionId, unitListIds, restoreCallback)); + dispatch(setUnitOrderListQuery(sectionId, subsectionId, unitListIds, restoreCallback)); }; useEffect(() => {