fix: unit pages ux bugs [FC-0083] (#1884)
This PR fixes some UX bugs related to the unit pages: * Sort for "recently modified" on unit tab does not update after adding new components to units * Change component delete warning message
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
import PropTypes from 'prop-types';
|
||||
import {
|
||||
ActionRow,
|
||||
Button,
|
||||
@@ -9,17 +8,29 @@ import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import messages from './messages';
|
||||
import LoadingButton from '../loading-button';
|
||||
|
||||
interface DeleteModalProps {
|
||||
isOpen: boolean;
|
||||
close: () => void;
|
||||
category?: string;
|
||||
onDeleteSubmit: () => void | Promise<void>;
|
||||
title?: string;
|
||||
description?: React.ReactNode | React.ReactNode[];
|
||||
variant?: string;
|
||||
btnLabel?: string;
|
||||
icon?: React.ElementType;
|
||||
}
|
||||
|
||||
const DeleteModal = ({
|
||||
category,
|
||||
category = '',
|
||||
isOpen,
|
||||
close,
|
||||
onDeleteSubmit,
|
||||
title,
|
||||
description,
|
||||
variant,
|
||||
variant = 'default',
|
||||
btnLabel,
|
||||
icon,
|
||||
}) => {
|
||||
}: DeleteModalProps) => {
|
||||
const intl = useIntl();
|
||||
|
||||
const modalTitle = title || intl.formatMessage(messages.title, { category });
|
||||
@@ -62,28 +73,4 @@ const DeleteModal = ({
|
||||
);
|
||||
};
|
||||
|
||||
DeleteModal.defaultProps = {
|
||||
category: '',
|
||||
title: '',
|
||||
description: '',
|
||||
variant: 'default',
|
||||
btnLabel: '',
|
||||
icon: null,
|
||||
};
|
||||
|
||||
DeleteModal.propTypes = {
|
||||
isOpen: PropTypes.bool.isRequired,
|
||||
close: PropTypes.func.isRequired,
|
||||
category: PropTypes.string,
|
||||
onDeleteSubmit: PropTypes.func.isRequired,
|
||||
title: PropTypes.string,
|
||||
description: PropTypes.oneOfType([
|
||||
PropTypes.element,
|
||||
PropTypes.string,
|
||||
]),
|
||||
variant: PropTypes.string,
|
||||
btnLabel: PropTypes.string,
|
||||
icon: PropTypes.elementType,
|
||||
};
|
||||
|
||||
export default DeleteModal;
|
||||
@@ -256,7 +256,7 @@ const LibraryAuthoringPage = ({
|
||||
[ContentType.units]: intl.formatMessage(messages.unitsTab),
|
||||
};
|
||||
const visibleTabsToRender = visibleTabs.map((contentType) => (
|
||||
<Tab eventKey={contentType} title={tabTitles[contentType]} />
|
||||
<Tab key={contentType} eventKey={contentType} title={tabTitles[contentType]} />
|
||||
));
|
||||
|
||||
return (
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import React, { useCallback, useContext } from 'react';
|
||||
import { FormattedMessage, useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Warning } from '@openedx/paragon/icons';
|
||||
import { Icon } from '@openedx/paragon';
|
||||
import { CalendarViewDay, School, Warning } from '@openedx/paragon/icons';
|
||||
|
||||
import { useSidebarContext } from '../common/context/SidebarContext';
|
||||
import { useDeleteLibraryBlock, useLibraryBlockMetadata, useRestoreLibraryBlock } from '../data/apiHooks';
|
||||
@@ -66,6 +67,22 @@ const ComponentDeleter = ({ usageKey, ...props }: Props) => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const deleteText = intl.formatMessage(messages.deleteComponentConfirm, {
|
||||
componentName: <b><BlockName usageKey={usageKey} /></b>,
|
||||
message: (
|
||||
<>
|
||||
<div className="d-flex mt-2">
|
||||
<Icon className="mr-2" src={School} />
|
||||
{intl.formatMessage(messages.deleteComponentConfirmMsg1)}
|
||||
</div>
|
||||
<div className="d-flex mt-2">
|
||||
<Icon className="mr-2" src={CalendarViewDay} />
|
||||
{intl.formatMessage(messages.deleteComponentConfirmMsg2)}
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
});
|
||||
|
||||
return (
|
||||
<DeleteModal
|
||||
isOpen
|
||||
@@ -73,16 +90,7 @@ const ComponentDeleter = ({ usageKey, ...props }: Props) => {
|
||||
variant="warning"
|
||||
title={intl.formatMessage(messages.deleteComponentWarningTitle)}
|
||||
icon={Warning}
|
||||
description={(
|
||||
<FormattedMessage
|
||||
{...messages.deleteComponentConfirm}
|
||||
values={{
|
||||
componentName: (
|
||||
<strong><BlockName usageKey={usageKey} /></strong>
|
||||
),
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
description={deleteText}
|
||||
onDeleteSubmit={doDelete}
|
||||
/>
|
||||
);
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { ReactNode, useCallback, useContext } from 'react';
|
||||
import { useCallback, useContext } from 'react';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Icon } from '@openedx/paragon';
|
||||
import { Warning, School, Widgets } from '@openedx/paragon/icons';
|
||||
@@ -47,7 +47,7 @@ const ContainerDeleter = ({
|
||||
</div>
|
||||
</>
|
||||
),
|
||||
}) as ReactNode as string;
|
||||
});
|
||||
const deleteSuccess = intl.formatMessage(messages.deleteUnitSuccess);
|
||||
const deleteError = intl.formatMessage(messages.deleteUnitFailed);
|
||||
const undoDeleteError = messages.undoDeleteUnitToastFailed;
|
||||
|
||||
@@ -68,9 +68,19 @@ const messages = defineMessages({
|
||||
},
|
||||
deleteComponentConfirm: {
|
||||
id: 'course-authoring.library-authoring.component.delete-confirmation-text',
|
||||
defaultMessage: 'Delete {componentName}? If this component has been used in a course, those copies won\'t be deleted, but they will no longer receive updates from the library.',
|
||||
defaultMessage: 'Delete {componentName}? {message}',
|
||||
description: 'Confirmation text to display before deleting a component',
|
||||
},
|
||||
deleteComponentConfirmMsg1: {
|
||||
id: 'course-authoring.library-authoring.component.delete-confirmation-msg-1',
|
||||
defaultMessage: 'If this component has been used in a course, those copies won\'t be deleted, but they will no longer receive updates from the library.',
|
||||
description: 'First part of confirmation message to display before deleting a component',
|
||||
},
|
||||
deleteComponentConfirmMsg2: {
|
||||
id: 'course-authoring.library-authoring.component.delete-confirmation-msg-2',
|
||||
defaultMessage: 'If this component has been used in any units, it will also be deleted from those units.',
|
||||
description: 'Second part of confirmation message to display before deleting a component',
|
||||
},
|
||||
deleteComponentCancelButton: {
|
||||
id: 'course-authoring.library-authoring.component.cancel-delete-button',
|
||||
defaultMessage: 'Cancel',
|
||||
|
||||
@@ -642,13 +642,22 @@ export const useAddComponentsToContainer = (containerId?: string) => {
|
||||
const queryClient = useQueryClient();
|
||||
return useMutation({
|
||||
mutationFn: async (componentIds: string[]) => {
|
||||
if (containerId !== undefined) {
|
||||
return api.addComponentsToContainer(containerId, componentIds);
|
||||
// istanbul ignore if: this should never happen
|
||||
if (!containerId) {
|
||||
return undefined;
|
||||
}
|
||||
return undefined;
|
||||
return api.addComponentsToContainer(containerId, componentIds);
|
||||
},
|
||||
onSettled: () => {
|
||||
queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.containerChildren(containerId!) });
|
||||
// istanbul ignore if: this should never happen
|
||||
if (!containerId) {
|
||||
return;
|
||||
}
|
||||
// NOTE: We invalidate the library query here because we need to update the library's
|
||||
// container list.
|
||||
const libraryId = getLibraryId(containerId);
|
||||
queryClient.invalidateQueries({ queryKey: libraryAuthoringQueryKeys.containerChildren(containerId) });
|
||||
queryClient.invalidateQueries({ predicate: (query) => libraryQueryPredicate(query, libraryId) });
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user