Skip to content

Commit 4717c7f

Browse files
committed
feat: save component order in unit
1 parent 948b48c commit 4717c7f

4 files changed

Lines changed: 62 additions & 8 deletions

File tree

src/library-authoring/data/api.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -648,3 +648,17 @@ export async function getLibraryContainerChildren(containerId: string): Promise<
648648
const { data } = await getAuthenticatedHttpClient().get(getLibraryContainerChildrenApiUrl(containerId));
649649
return camelCaseObject(data);
650650
}
651+
652+
/**
653+
* Update library container's children.
654+
*/
655+
export async function updateLibraryContainerChildren(
656+
containerId: string,
657+
children: string[],
658+
): Promise<LibraryBlockMetadata[]> {
659+
const { data } = await getAuthenticatedHttpClient().patch(
660+
getLibraryContainerChildrenApiUrl(containerId),
661+
{'usage_keys': children},
662+
);
663+
return camelCaseObject(data);
664+
}

src/library-authoring/data/apiHooks.ts

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import {
5454
type UpdateContainerDataRequest,
5555
restoreContainer,
5656
getLibraryContainerChildren,
57+
updateLibraryContainerChildren,
5758
} from './api';
5859
import { VersionSpec } from '../LibraryBlock';
5960

@@ -669,3 +670,25 @@ export const useContainerChildren = (libraryId?: string, containerId?: string) =
669670
queryFn: () => getLibraryContainerChildren(containerId!),
670671
})
671672
);
673+
674+
/**
675+
* Update container children
676+
*/
677+
export const useUpdateContainerChildren = (containerId?: string) => {
678+
const queryClient = useQueryClient();
679+
return useMutation({
680+
mutationFn: async (usageKeys: string[]) => {
681+
if (!containerId) {
682+
return undefined;
683+
}
684+
return updateLibraryContainerChildren(containerId, usageKeys)
685+
},
686+
onSettled: () => {
687+
// NOTE: We invalidate the library query here because we need to update the library's
688+
// container list.
689+
const libraryId = getLibraryId(containerId!);
690+
queryClient.invalidateQueries({ predicate: (query) => libraryQueryPredicate(query, libraryId) });
691+
queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.container(libraryId, containerId) });
692+
},
693+
});
694+
};

src/library-authoring/units/LibraryUnitBlocks.tsx

Lines changed: 15 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1-
import { FormattedMessage } from '@edx/frontend-platform/i18n';
1+
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
22
import {
33
ActionRow, Badge, Icon, IconButton, Stack, useToggle,
44
} from '@openedx/paragon';
55
import { Description, DragIndicator } from '@openedx/paragon/icons';
66
import { useQueryClient } from '@tanstack/react-query';
77
import classNames from 'classnames';
8-
import { useEffect, useState } from 'react';
8+
import { useContext, useEffect, useState } from 'react';
99
import { ContentTagsDrawerSheet } from '../../content-tags-drawer';
1010
import { blockTypes } from '../../editors/data/constants/app';
1111
import DraggableList, { SortableItem } from '../../generic/DraggableList';
@@ -18,10 +18,11 @@ import TagCount from '../../generic/tag-count';
1818
import { useLibraryContext } from '../common/context/LibraryContext';
1919
import ComponentMenu from '../components';
2020
import { LibraryBlockMetadata } from '../data/api';
21-
import { libraryAuthoringQueryKeys, useContainerChildren } from '../data/apiHooks';
21+
import { libraryAuthoringQueryKeys, useContainerChildren, useUpdateContainerChildren } from '../data/apiHooks';
2222
import { LibraryBlock } from '../LibraryBlock';
2323
import { useLibraryRoutes } from '../routes';
2424
import messages from './messages';
25+
import { ToastContext } from '../../generic/toast-context';
2526

2627
interface LibraryUnitBlocksProps {
2728
/** set to true if it is rendered as preview
@@ -31,10 +32,12 @@ interface LibraryUnitBlocksProps {
3132
}
3233

3334
export const LibraryUnitBlocks = ({ preview }: LibraryUnitBlocksProps) => {
35+
const intl = useIntl();
3436
const [orderedBlocks, setOrderedBlocks] = useState<LibraryBlockMetadata[]>([]);
3537
const [isManageTagsDrawerOpen, openManageTagsDrawer, closeManageTagsDrawer] = useToggle(false);
3638
const [hidePreviewFor, setHidePreviewFor] = useState<string | null>(null);
3739
const { navigateTo } = useLibraryRoutes();
40+
const { showToast } = useContext(ToastContext);
3841

3942
const {
4043
libraryId,
@@ -45,6 +48,7 @@ export const LibraryUnitBlocks = ({ preview }: LibraryUnitBlocksProps) => {
4548
} = useLibraryContext();
4649

4750
const queryClient = useQueryClient();
51+
const orderMutator = useUpdateContainerChildren(unitId);
4852
const {
4953
data: blocks,
5054
isLoading,
@@ -63,11 +67,14 @@ export const LibraryUnitBlocks = ({ preview }: LibraryUnitBlocksProps) => {
6367
return <ErrorAlert error={error} />;
6468
}
6569

66-
/* istanbul ignore next */
67-
const handleReorder = () => (newOrder: LibraryBlockMetadata[]) => {
68-
// eslint-disable-next-line no-console
69-
console.log('LibraryUnitBlocks newOrder: ', newOrder);
70-
// TODO: update order of components in unit
70+
const handleReorder = () => async (newOrder: LibraryBlockMetadata[]) => {
71+
const usageKeys = newOrder.map((o) => o.id);
72+
try {
73+
await orderMutator.mutateAsync(usageKeys);
74+
showToast(intl.formatMessage(messages.orderUpdatedMsg));
75+
} catch (e) {
76+
showToast(intl.formatMessage(messages.failedOrderUpdatedMsg));
77+
}
7178
};
7279

7380
const onTagSidebarClose = () => {

src/library-authoring/units/messages.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ const messages = defineMessages({
2121
defaultMessage: 'Draft',
2222
description: 'Chip in components in unit page that is shown when component has unpublished changes',
2323
},
24+
orderUpdatedMsg: {
25+
id: 'course-authoring.library-authoring.unit-component.order-updated-msg.text',
26+
defaultMessage: 'Order updated',
27+
description: 'Toast message displayed when components are successfully reordered in a unit',
28+
},
29+
failedOrderUpdatedMsg: {
30+
id: 'course-authoring.library-authoring.unit-component.failed-order-updated-msg.text',
31+
defaultMessage: 'Failed to update components order',
32+
description: 'Toast message displayed when components are successfully reordered in a unit',
33+
},
2434
});
2535

2636
export default messages;

0 commit comments

Comments
 (0)