Skip to content

Commit

Permalink
feat: Course Outline Reindex (#55)
Browse files Browse the repository at this point in the history
* feat: [2u-277] add alerts

* feat: [2u-277] add translates

* feat: [2u-277] fix tests

* fix: [2u-277] fix slice and hook

---------

Co-authored-by: Vladislav Keblysh <[email protected]>
  • Loading branch information
2 people authored and navinkarkera committed Nov 22, 2023
1 parent e06ce7c commit ab94d6d
Show file tree
Hide file tree
Showing 9 changed files with 152 additions and 18 deletions.
40 changes: 39 additions & 1 deletion src/course-outline/CourseOutline.jsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,20 @@
import React from 'react';
import PropTypes from 'prop-types';
import { useIntl } from '@edx/frontend-platform/i18n';
import { Container, Layout } from '@edx/paragon';
import {
Container,
Layout,
TransitionReplace,
} from '@edx/paragon';
import {
CheckCircle as CheckCircleIcon,
Warning as WarningIcon,
} from '@edx/paragon/icons';

import SubHeader from '../generic/sub-header/SubHeader';
import { RequestStatus } from '../data/constants';
import InternetConnectionAlert from '../generic/internet-connection-alert';
import AlertMessage from '../generic/alert-message';
import HeaderNavigations from './header-navigations/HeaderNavigations';
import OutlineSideBar from './outline-sidebar/OutlineSidebar';
import messages from './messages';
Expand All @@ -21,9 +30,12 @@ const CourseOutline = ({ courseId }) => {
statusBarData,
isLoading,
isReIndexShow,
showErrorAlert,
showSuccessAlert,
isSectionsExpanded,
isEnableHighlightsModalOpen,
isInternetConnectionAlertFailed,
isDisabledReindexButton,
headerNavigationsActions,
openEnableHighlightsModal,
closeEnableHighlightsModal,
Expand All @@ -40,6 +52,21 @@ const CourseOutline = ({ courseId }) => {
<>
<Container size="xl" className="m-4">
<section className="course-outline-container mb-4 mt-5">
<TransitionReplace>
{showSuccessAlert ? (
<AlertMessage
key={intl.formatMessage(messages.alertSuccessAriaLabelledby)}
show={showSuccessAlert}
variant="success"
icon={CheckCircleIcon}
title={intl.formatMessage(messages.alertSuccessTitle)}
description={intl.formatMessage(messages.alertSuccessDescription)}
aria-hidden="true"
aria-labelledby={intl.formatMessage(messages.alertSuccessAriaLabelledby)}
aria-describedby={intl.formatMessage(messages.alertSuccessAriaDescribedby)}
/>
) : null}
</TransitionReplace>
<SubHeader
className="mt-5"
title={intl.formatMessage(messages.headingTitle)}
Expand All @@ -50,6 +77,7 @@ const CourseOutline = ({ courseId }) => {
isReIndexShow={isReIndexShow}
isSectionsExpanded={isSectionsExpanded}
headerNavigationsActions={headerNavigationsActions}
isDisabledReindexButton={isDisabledReindexButton}
/>
)}
/>
Expand Down Expand Up @@ -92,6 +120,16 @@ const CourseOutline = ({ courseId }) => {
isQueryPending={savingStatus === RequestStatus.PENDING}
onInternetConnectionFailed={handleInternetConnectionFailed}
/>
{showErrorAlert && (
<AlertMessage
key={intl.formatMessage(messages.alertErrorTitle)}
show={showErrorAlert}
variant="danger"
icon={WarningIcon}
title={intl.formatMessage(messages.alertErrorTitle)}
aria-hidden="true"
/>
)}
</div>
</>
);
Expand Down
13 changes: 13 additions & 0 deletions src/course-outline/data/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ const getEnableHighlightsEmailsApiUrl = (courseId) => {
const formattedCourseId = courseId.split('course-v1:')[1];
return `${getApiBaseUrl()}/xblock/block-v1:${formattedCourseId}+type@course+block@course`;
};
export const getCourseReindexApiUrl = (reindexLink) => `${getApiBaseUrl()}${reindexLink}`;

/**
* Get course outline index.
Expand Down Expand Up @@ -87,3 +88,15 @@ export async function enableCourseHighlightsEmails(courseId) {

return data;
}

/**
* Restart reindex course
* @param {string} reindexLink
* @returns {Promise<Object>}
*/
export async function restartIndexingOnCourse(reindexLink) {
const { data } = await getAuthenticatedHttpClient()
.get(getCourseReindexApiUrl(reindexLink));

return camelCaseObject(data);
}
2 changes: 1 addition & 1 deletion src/course-outline/data/selectors.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
export const getOutlineIndexData = (state) => state.courseOutline.outlineIndexData;
export const getLoadingOutlineIndexStatus = (state) => state.courseOutline.loadingOutlineIndexStatus;
export const getLoadingStatus = (state) => state.courseOutline.loadingStatus;
export const getStatusBarData = (state) => state.courseOutline.statusBarData;
export const getSavingStatus = (state) => state.courseOutline.savingStatus;
21 changes: 17 additions & 4 deletions src/course-outline/data/slice.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,10 @@ import { RequestStatus } from '../../data/constants';
const slice = createSlice({
name: 'courseOutline',
initialState: {
loadingOutlineIndexStatus: RequestStatus.IN_PROGRESS,
loadingStatus: {
outlineIndexLoadingStatus: RequestStatus.IN_PROGRESS,
reIndexLoadingStatus: RequestStatus.IN_PROGRESS,
},
outlineIndexData: {},
savingStatus: '',
statusBarData: {
Expand All @@ -26,8 +29,17 @@ const slice = createSlice({
fetchOutlineIndexSuccess: (state, { payload }) => {
state.outlineIndexData = payload;
},
updateLoadingOutlineIndexStatus: (state, { payload }) => {
state.loadingOutlineIndexStatus = payload.status;
updateOutlineIndexLoadingStatus: (state, { payload }) => {
state.loadingStatus = {
...state.loadingStatus,
outlineIndexLoadingStatus: payload.status,
};
},
updateReindexLoadingStatus: (state, { payload }) => {
state.loadingStatus = {
...state.loadingStatus,
reIndexLoadingStatus: payload.status,
};
},
updateStatusBar: (state, { payload }) => {
state.statusBarData = {
Expand All @@ -52,7 +64,8 @@ const slice = createSlice({

export const {
fetchOutlineIndexSuccess,
updateLoadingOutlineIndexStatus,
updateOutlineIndexLoadingStatus,
updateReindexLoadingStatus,
updateStatusBar,
fetchStatusBarChecklistSuccess,
fetchStatusBarSelPacedSuccess,
Expand Down
27 changes: 23 additions & 4 deletions src/course-outline/data/thunk.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,12 @@ import {
getCourseBestPractices,
getCourseLaunch,
getCourseOutlineIndex,
restartIndexingOnCourse,
} from './api';
import {
fetchOutlineIndexSuccess,
updateLoadingOutlineIndexStatus,
updateOutlineIndexLoadingStatus,
updateReindexLoadingStatus,
updateStatusBar,
fetchStatusBarChecklistSuccess,
fetchStatusBarSelPacedSuccess,
Expand All @@ -20,18 +22,18 @@ import {

export function fetchCourseOutlineIndexQuery(courseId) {
return async (dispatch) => {
dispatch(updateLoadingOutlineIndexStatus({ status: RequestStatus.IN_PROGRESS }));
dispatch(updateOutlineIndexLoadingStatus({ status: RequestStatus.IN_PROGRESS }));

try {
const outlineIndex = await getCourseOutlineIndex(courseId);
const { courseReleaseDate, courseStructure: { highlightsEnabledForMessaging, highlightsDocUrl } } = outlineIndex;
dispatch(fetchOutlineIndexSuccess(outlineIndex));
dispatch(updateStatusBar({ courseReleaseDate, highlightsEnabledForMessaging, highlightsDocUrl }));

dispatch(updateLoadingOutlineIndexStatus({ status: RequestStatus.SUCCESSFUL }));
dispatch(updateOutlineIndexLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch (error) {
dispatch(updateLoadingOutlineIndexStatus({ status: RequestStatus.FAILED }));
dispatch(updateOutlineIndexLoadingStatus({ status: RequestStatus.FAILED }));
return false;
}
};
Expand Down Expand Up @@ -91,3 +93,20 @@ export function enableCourseHighlightsEmailsQuery(courseId) {
}
};
}

export function fetchCourseReindexQuery(courseId, reindexLink) {
return async (dispatch) => {
dispatch(updateReindexLoadingStatus({ status: RequestStatus.IN_PROGRESS }));

try {
await restartIndexingOnCourse(reindexLink);
dispatch(updateReindexLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
// dispatch(fetchCourseOutlineIndexQuery(courseId));

return true;
} catch (error) {
dispatch(updateReindexLoadingStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ describe('<EnableHighlightsModal />', () => {

const submitButton = getByRole('button', { name: messages.submitButton.defaultMessage });
fireEvent.click(submitButton);
expect(onEnableHighlightsSubmitMock).toHaveBeenCalledTimes();
expect(onEnableHighlightsSubmitMock).toHaveBeenCalled();
});

it('calls the close function when the "Cancel" button is clicked', () => {
Expand Down
13 changes: 10 additions & 3 deletions src/course-outline/header-navigations/HeaderNavigations.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,12 @@ import {

import messages from './messages';

const HeaderNavigations = ({ headerNavigationsActions, isReIndexShow, isSectionsExpanded }) => {
const HeaderNavigations = ({
headerNavigationsActions,
isReIndexShow,
isSectionsExpanded,
isDisabledReindexButton,
}) => {
const intl = useIntl();
const {
handleNewSection, handleReIndex, handleExpandAll, handleViewLive,
Expand All @@ -35,15 +40,16 @@ const HeaderNavigations = ({ headerNavigationsActions, isReIndexShow, isSections
{isReIndexShow && (
<OverlayTrigger
placement="bottom"
overlay={(
overlay={!isDisabledReindexButton ? (
<Tooltip id={intl.formatMessage(messages.reindexButtonTooltip)}>
{intl.formatMessage(messages.reindexButtonTooltip)}
</Tooltip>
)}
) : <React.Fragment key="reindex close" />}
>
<Button
onClick={handleReIndex}
variant="outline-primary"
disabled={isDisabledReindexButton}
>
{intl.formatMessage(messages.reindexButton)}
</Button>
Expand Down Expand Up @@ -81,6 +87,7 @@ const HeaderNavigations = ({ headerNavigationsActions, isReIndexShow, isSections
HeaderNavigations.propTypes = {
isReIndexShow: PropTypes.bool.isRequired,
isSectionsExpanded: PropTypes.bool.isRequired,
isDisabledReindexButton: PropTypes.bool.isRequired,
headerNavigationsActions: PropTypes.shape({
handleNewSection: PropTypes.func.isRequired,
handleReIndex: PropTypes.func.isRequired,
Expand Down
32 changes: 28 additions & 4 deletions src/course-outline/hooks.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,8 +3,9 @@ import { useDispatch, useSelector } from 'react-redux';
import { useToggle } from '@edx/paragon';

import { RequestStatus } from '../data/constants';
import { updateSavingStatus } from './data/slice';
import {
getLoadingOutlineIndexStatus,
getLoadingStatus,
getOutlineIndexData,
getSavingStatus,
getStatusBarData,
Expand All @@ -14,25 +15,35 @@ import {
fetchCourseBestPracticesQuery,
fetchCourseLaunchQuery,
fetchCourseOutlineIndexQuery,
fetchCourseReindexQuery,
} from './data/thunk';
import { updateSavingStatus } from './data/slice';

const useCourseOutline = ({ courseId }) => {
const dispatch = useDispatch();

const { reindexLink } = useSelector(getOutlineIndexData);
const { outlineIndexLoadingStatus } = useSelector(getLoadingOutlineIndexStatus);
const { outlineIndexLoadingStatus, reIndexLoadingStatus } = useSelector(getLoadingStatus);
const statusBarData = useSelector(getStatusBarData);
const savingStatus = useSelector(getSavingStatus);

const [isEnableHighlightsModalOpen, openEnableHighlightsModal, closeEnableHighlightsModal] = useToggle(false);
const [isSectionsExpanded, setSectionsExpanded] = useState(false);
const [isDisabledReindexButton, setDisableReindexButton] = useState(false);
const [showSuccessAlert, setShowSuccessAlert] = useState(false);
const [showErrorAlert, setShowErrorAlert] = useState(false);

const headerNavigationsActions = {
handleNewSection: () => {
// TODO add handler
},
handleReIndex: () => {
// TODO add handler
setDisableReindexButton(true);
setShowSuccessAlert(false);
setShowErrorAlert(false);

dispatch(fetchCourseReindexQuery(courseId, reindexLink)).then(() => {
setDisableReindexButton(false);
});
},
handleExpandAll: () => {
setSectionsExpanded((prevState) => !prevState);
Expand All @@ -58,10 +69,23 @@ const useCourseOutline = ({ courseId }) => {
dispatch(fetchCourseLaunchQuery({ courseId }));
}, [courseId]);

useEffect(() => {
if (reIndexLoadingStatus === RequestStatus.FAILED) {
setShowErrorAlert(true);
}

if (reIndexLoadingStatus === RequestStatus.SUCCESSFUL) {
setShowSuccessAlert(true);
}
}, [reIndexLoadingStatus]);

return {
savingStatus,
isLoading: outlineIndexLoadingStatus === RequestStatus.IN_PROGRESS,
isReIndexShow: Boolean(reindexLink),
showSuccessAlert,
showErrorAlert,
isDisabledReindexButton,
isSectionsExpanded,
headerNavigationsActions,
handleEnableHighlightsSubmit,
Expand Down
20 changes: 20 additions & 0 deletions src/course-outline/messages.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,26 @@ const messages = defineMessages({
id: 'course-authoring.course-outline.subTitle',
defaultMessage: 'Content',
},
alertSuccessTitle: {
id: 'course-authoring.course-outline.reindex.alert.success.title',
defaultMessage: 'Course index',
},
alertSuccessDescription: {
id: 'course-authoring.course-outline.reindex.alert.success.description',
defaultMessage: 'Course has been successfully reindexed.',
},
alertSuccessAriaLabelledby: {
id: 'course-authoring.course-outline.reindex.alert.success.aria.labelledby',
defaultMessage: 'alert-confirmation-title',
},
alertSuccessAriaDescribedby: {
id: 'course-authoring.course-outline.reindex.alert.success.aria.describedby',
defaultMessage: 'alert-confirmation-description',
},
alertErrorTitle: {
id: 'course-authoring.course-outline.reindex.alert.error.title',
defaultMessage: 'There were errors reindexing course.',
},
});

export default messages;

0 comments on commit ab94d6d

Please sign in to comment.