Skip to content

Commit 82db05a

Browse files
committed
refactor(course-outline): expose module interfaces and trim reorder API
Route local course-outline imports through public state/data index files. Move reorder preview math to callers so commit handlers own mutation and cache-sync work. Also remove stale hook deps and an unsafe outline data cast.
1 parent c469dc8 commit 82db05a

20 files changed

Lines changed: 179 additions & 333 deletions

src/course-outline/CourseOutline.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -33,7 +33,9 @@ import {
3333
getCourseItemApiUrl,
3434
getXBlockBaseApiUrl,
3535
exportTags,
36-
} from './data/api';
36+
courseOutlineIndexQueryKey,
37+
courseOutlineQueryKeys,
38+
} from './data';
3739

3840
import {
3941
courseOutlineIndexMock as originalCourseOutlineIndexMock,
@@ -44,8 +46,6 @@ import {
4446
courseSubsectionMock,
4547
} from './__mocks__';
4648
import { COURSE_BLOCK_NAMES, VIDEO_SHARING_OPTIONS } from './constants';
47-
import { courseOutlineIndexQueryKey } from './data/outlineIndexQuery';
48-
import { courseOutlineQueryKeys } from './data/apiHooks';
4949
import CourseOutline from './CourseOutline';
5050

5151
import messages from './messages';

src/course-outline/CourseOutline.tsx

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -32,20 +32,19 @@ import {
3232
useSetVideoSharingOption,
3333
useDismissNotification,
3434
useRestartIndexingOnCourse,
35-
} from '@src/course-outline/data/apiHooks';
35+
} from '@src/course-outline/data';
3636
import { useCourseOutlineContext } from './CourseOutlineContext';
3737
import { COURSE_BLOCK_NAMES } from './constants';
3838

3939
import PageAlerts from './page-alerts/PageAlerts';
4040

41-
import type { CourseOutline as CourseOutlineData } from './data/types';
4241
import OutlineTree from './OutlineTree';
43-
import { useOutlineModals } from './state/useOutlineModals';
42+
import { useOutlineModals } from './state';
4443
import OutlineModals from './OutlineModals';
4544

4645
import messages from './messages';
4746
import headerMessages from './header-navigations/messages';
48-
import { getTagsExportFile } from './data/api';
47+
import { getTagsExportFile } from './data';
4948
import { StatusBar } from './status-bar/StatusBar';
5049

5150
const CourseOutline = () => {
@@ -57,9 +56,6 @@ const CourseOutline = () => {
5756
const {
5857
courseUsageKey,
5958
sections,
60-
updateSectionOrderByIndex,
61-
updateSubsectionOrderByIndex,
62-
updateUnitOrderByIndex,
6359
previewSections,
6460
cancelReorderPreview,
6561
commitSectionReorder,
@@ -79,17 +75,15 @@ const CourseOutline = () => {
7975
outlineIndexData,
8076
} = useCourseOutlineContext();
8177

82-
const {
83-
reindexLink,
84-
lmsLink,
85-
notificationDismissUrl,
86-
discussionsSettings,
87-
discussionsIncontextLearnmoreUrl,
88-
deprecatedBlocksInfo,
89-
proctoringErrors,
90-
mfeProctoredExamSettingsUrl,
91-
advanceSettingsUrl,
92-
} = (outlineIndexData || {}) as CourseOutlineData;
78+
const reindexLink = outlineIndexData?.reindexLink;
79+
const lmsLink = outlineIndexData?.lmsLink;
80+
const notificationDismissUrl = outlineIndexData?.notificationDismissUrl;
81+
const discussionsSettings = outlineIndexData?.discussionsSettings;
82+
const discussionsIncontextLearnmoreUrl = outlineIndexData?.discussionsIncontextLearnmoreUrl;
83+
const deprecatedBlocksInfo = outlineIndexData?.deprecatedBlocksInfo;
84+
const proctoringErrors = outlineIndexData?.proctoringErrors;
85+
const mfeProctoredExamSettingsUrl = outlineIndexData?.mfeProctoredExamSettingsUrl;
86+
const advanceSettingsUrl = outlineIndexData?.advanceSettingsUrl;
9387

9488
const { reIndexLoadingStatus } = loadingStatus || {};
9589

@@ -151,7 +145,7 @@ const CourseOutline = () => {
151145
handleExpandAll: () => {
152146
setSectionsExpanded((prevState) => !prevState);
153147
},
154-
lmsLink,
148+
lmsLink: lmsLink ?? '',
155149
}), [handleAddBlock, courseUsageKey, reindexLink, reindexMutation, lmsLink]);
156150

157151
// ─── Effects (previously in hooks.jsx) ───────────────────────────────────
@@ -313,9 +307,6 @@ const CourseOutline = () => {
313307
commitSectionReorder={commitSectionReorder}
314308
commitSubsectionReorder={commitSubsectionReorder}
315309
commitUnitReorder={commitUnitReorder}
316-
updateSectionOrderByIndex={updateSectionOrderByIndex}
317-
updateSubsectionOrderByIndex={updateSubsectionOrderByIndex}
318-
updateUnitOrderByIndex={updateUnitOrderByIndex}
319310
handleOpenHighlightsModal={handleOpenHighlightsModal}
320311
openConfigureModal={handleOpenConfigureModal}
321312
openDeleteModal={openDeleteModal}

src/course-outline/CourseOutlineContext.test.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
waitFor,
66
} from '@src/testUtils';
77
import { courseOutlineIndexMock } from './__mocks__';
8-
import { getCourseOutlineIndexApiUrl } from './data/api';
8+
import { getCourseOutlineIndexApiUrl } from './data';
99
import {
1010
CourseOutlineProvider,
1111
useCourseOutlineContext,

src/course-outline/CourseOutlineContext.tsx

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -14,38 +14,33 @@ import type {
1414
XBlockActions,
1515
} from '@src/data/types';
1616

17-
import { useCourseItemData, useCourseOutlineSavingStatus, useCourseOutlineReindexStatus } from './data/apiHooks';
17+
import { useCourseItemData, useCourseOutlineSavingStatus, useCourseOutlineReindexStatus } from './data';
1818

1919
import { useToggleWithValue } from '@src/hooks';
20-
import { useOutlineReorderState } from './state/useOutlineReorderState';
21-
import { useOutlineStatusState } from './state/useOutlineStatusState';
2220
import {
21+
useOutlineReorderState,
22+
useOutlineStatusState,
2323
computeErrorSignature,
2424
filterDismissedErrors,
2525
pruneDismissedErrorSignatures,
26-
} from './state/outlineErrorDismissal';
27-
import {
2826
EditableSubsection,
2927
getLastEditableItem,
3028
getLastEditableSubsection,
31-
} from './state/editability';
29+
} from './state';
3230
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
3331
import type { ModalState } from '@src/CourseAuthoringContext';
3432

3533
import {
3634
CourseOutline,
3735
CourseOutlineState as LegacyCourseOutlineState,
3836
CourseOutlineStatusBar,
39-
} from './data/types';
37+
} from './data';
4038

4139
type CourseOutlineContextData = {
4240
outlineIndexData: CourseOutline | undefined;
4341
courseName?: string;
4442
courseUsageKey: string;
4543
sections: XBlock[];
46-
updateSectionOrderByIndex: (currentIndex: number, newIndex: number) => Promise<void>;
47-
updateSubsectionOrderByIndex: (section: XBlock, moveDetails: any) => Promise<void>;
48-
updateUnitOrderByIndex: (section: XBlock, moveDetails: any) => Promise<void>;
4944
courseActions: XBlockActions;
5045
statusBarData: CourseOutlineStatusBar;
5146
savingStatus: string;
@@ -127,9 +122,6 @@ export const CourseOutlineProvider = ({ children }: { children?: React.ReactNode
127122
commitSectionReorder,
128123
commitSubsectionReorder,
129124
commitUnitReorder,
130-
updateSectionOrderByIndex,
131-
updateSubsectionOrderByIndex,
132-
updateUnitOrderByIndex,
133125
} = useOutlineReorderState({ courseId, sections });
134126

135127
const [currentSelection, setCurrentSelection] = useState<SelectionState | undefined>();
@@ -245,9 +237,6 @@ export const CourseOutlineProvider = ({ children }: { children?: React.ReactNode
245237
courseName: effectiveOutlineIndexData?.courseStructure?.displayName,
246238
courseUsageKey: effectiveOutlineIndexData?.courseStructure?.id || courseId,
247239
sections: visibleSections,
248-
updateSectionOrderByIndex,
249-
updateSubsectionOrderByIndex,
250-
updateUnitOrderByIndex,
251240
courseActions,
252241
statusBarData,
253242
savingStatus,
@@ -284,9 +273,6 @@ export const CourseOutlineProvider = ({ children }: { children?: React.ReactNode
284273
effectiveOutlineIndexData,
285274
courseId,
286275
visibleSections,
287-
updateSectionOrderByIndex,
288-
updateSubsectionOrderByIndex,
289-
updateUnitOrderByIndex,
290276
courseActions,
291277
statusBarData,
292278
savingStatus,

src/course-outline/CourseOutlineStateContext.test.tsx

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,8 +13,7 @@ import {
1313
CourseOutlineProvider,
1414
useCourseOutlineContext,
1515
} from './CourseOutlineContext';
16-
import { courseOutlineIndexQueryKey } from './data/outlineIndexQuery';
17-
import { getCourseOutlineIndexApiUrl } from './data/api';
16+
import { courseOutlineIndexQueryKey, getCourseOutlineIndexApiUrl } from './data';
1817

1918
let currentItemData;
2019
const mockOutlineIndexData = {

src/course-outline/OutlineTree.tsx

Lines changed: 49 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
import { useCallback } from 'react';
2+
import { arrayMove } from '@dnd-kit/sortable';
13
import { SortableContext, verticalListSortingStrategy } from '@dnd-kit/sortable';
24
import { ContainerType } from '@src/generic/key-utils';
35
import type { OutlineActionSelection, XBlock, XBlockActions } from '@src/data/types';
@@ -10,6 +12,8 @@ import OutlineAddChildButtons from './OutlineAddChildButtons';
1012
import DraggableList from './drag-helper/DraggableList';
1113
import {
1214
canMoveSection,
15+
moveSubsection,
16+
moveUnit,
1317
possibleUnitMoves,
1418
possibleSubsectionMoves,
1519
} from './drag-helper/utils';
@@ -37,9 +41,6 @@ export interface OutlineTreeProps {
3741
subsectionId: string,
3842
unitListIds: string[],
3943
) => Promise<void>;
40-
updateSectionOrderByIndex: (currentIndex: number, newIndex: number) => Promise<void>;
41-
updateSubsectionOrderByIndex: (section: XBlock, moveDetails: any) => Promise<void>;
42-
updateUnitOrderByIndex: (section: XBlock, moveDetails: any) => Promise<void>;
4344
handleOpenHighlightsModal: (section: XBlock) => void;
4445
openConfigureModal: (selection: OutlineActionSelection) => void;
4546
openDeleteModal: (selection: OutlineActionSelection) => void;
@@ -60,15 +61,50 @@ const OutlineTree = ({
6061
commitSectionReorder,
6162
commitSubsectionReorder,
6263
commitUnitReorder,
63-
updateSectionOrderByIndex,
64-
updateSubsectionOrderByIndex,
65-
updateUnitOrderByIndex,
6664
handleOpenHighlightsModal,
6765
openConfigureModal,
6866
openDeleteModal,
6967
handlePasteClipboardClick,
70-
}: OutlineTreeProps) => (
71-
<section>
68+
}: OutlineTreeProps) => {
69+
// ─── Card order change handlers (preview + commit) ────────────────────
70+
const handleSectionOrderChange = useCallback(async (oldIndex: number, newIndex: number) => {
71+
if (oldIndex === newIndex) { return; }
72+
const nextSections = arrayMove(sections, oldIndex, newIndex) as XBlock[];
73+
const sectionListIds = nextSections.map((s) => s.id);
74+
previewSections(nextSections);
75+
await commitSectionReorder(sectionListIds);
76+
}, [sections, previewSections, commitSectionReorder]);
77+
78+
const handleSubsectionOrderChange = useCallback(async (section: XBlock, moveDetails: any) => {
79+
const { fn, args, sectionId } = moveDetails;
80+
if (!args) { return; }
81+
const [sectionsCopy, newSubsections] = fn(...args);
82+
if (newSubsections && sectionId) {
83+
previewSections(sectionsCopy);
84+
await commitSubsectionReorder(
85+
sectionId,
86+
section.id,
87+
newSubsections.map((s: XBlock) => s.id),
88+
);
89+
}
90+
}, [previewSections, commitSubsectionReorder]);
91+
92+
const handleUnitOrderChange = useCallback(async (section: XBlock, moveDetails: any) => {
93+
const { fn, args, sectionId, subsectionId } = moveDetails;
94+
if (!args) { return; }
95+
const [sectionsCopy, newUnits] = fn(...args);
96+
if (newUnits && subsectionId) {
97+
previewSections(sectionsCopy);
98+
await commitUnitReorder(
99+
sectionId,
100+
section.id,
101+
subsectionId,
102+
newUnits.map((u: XBlock) => u.id),
103+
);
104+
}
105+
}, [previewSections, commitUnitReorder]);
106+
107+
return (<section>
72108
{!hasOutlineIndexError && (
73109
<div className="pt-4">
74110
{sections.length ? (
@@ -98,7 +134,7 @@ const OutlineTree = ({
98134
onOpenConfigureModal={openConfigureModal}
99135
onOpenDeleteModal={openDeleteModal}
100136
isSectionsExpanded={isSectionsExpanded}
101-
onOrderChange={updateSectionOrderByIndex}
137+
onOrderChange={handleSectionOrderChange}
102138
>
103139
<SortableContext
104140
id={section.id}
@@ -122,7 +158,7 @@ const OutlineTree = ({
122158
isCustomRelativeDatesActive={isCustomRelativeDatesActive}
123159
onOpenDeleteModal={openDeleteModal}
124160
onOpenConfigureModal={openConfigureModal}
125-
onOrderChange={updateSubsectionOrderByIndex}
161+
onOrderChange={handleSubsectionOrderChange}
126162
onPasteClick={handlePasteClipboardClick}
127163
>
128164
<SortableContext
@@ -149,7 +185,7 @@ const OutlineTree = ({
149185
)}
150186
onOpenConfigureModal={openConfigureModal}
151187
onOpenDeleteModal={openDeleteModal}
152-
onOrderChange={updateUnitOrderByIndex}
188+
onOrderChange={handleUnitOrderChange}
153189
discussionsSettings={discussionsSettings}
154190
/>
155191
))}
@@ -185,6 +221,7 @@ const OutlineTree = ({
185221
</div>
186222
)}
187223
</section>
188-
);
224+
);
225+
};
189226

190227
export default OutlineTree;

src/course-outline/data/index.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
export * from './api';
2+
export * from './apiHooks';
3+
export * from './outlineIndexQuery';
4+
export * from './types';

src/course-outline/outline-sidebar/OutlineHelpSidebar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import { SidebarContent, SidebarSection, SidebarTitle } from '@src/generic/sideb
66
import { useHelpUrls } from '@src/help-urls/hooks';
77
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
88

9-
import { useCourseDetails } from '../data/apiHooks';
9+
import { useCourseDetails } from '../data';
1010
import { getFormattedSidebarMessages } from './utils';
1111

1212
const OutlineHelpSideBar = () => {

src/course-outline/outline-sidebar/info-sidebar/InfoSection.tsx

Lines changed: 1 addition & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,6 @@ import { SidebarContent, SidebarSection } from '@src/generic/sidebar';
1212
import { useGetBlockTypes } from '@src/search-manager';
1313
import { useQueryClient } from '@tanstack/react-query';
1414
import { useCallback } from 'react';
15-
import { useDispatch } from 'react-redux';
1615
import { LibraryReferenceCard } from '@src/generic/library-reference-card/LibraryReferenceCard';
1716
import messages from '../messages';
1817

@@ -22,7 +21,6 @@ interface Props {
2221

2322
export const InfoSection = ({ itemId }: Props) => {
2423
const intl = useIntl();
25-
const dispatch = useDispatch();
2624
const queryClient = useQueryClient();
2725
const { data: itemData } = useCourseItemData(itemId);
2826
const { data: componentData } = useGetBlockTypes(
@@ -52,7 +50,7 @@ export const InfoSection = ({ itemId }: Props) => {
5250
if (courseId) {
5351
invalidateLinksQuery(queryClient, courseId);
5452
}
55-
}, [dispatch, selectedContainerState, queryClient, courseId]);
53+
}, [selectedContainerState, queryClient, courseId]);
5654

5755
/* istanbul ignore next */
5856
if (!itemData) {

0 commit comments

Comments
 (0)