refactor: Move redux to react query in course checklist (#2870)
This commit is contained in:
@@ -1,16 +1,12 @@
|
||||
import {
|
||||
render,
|
||||
waitFor,
|
||||
screen,
|
||||
initializeMocks,
|
||||
} from '@src/testUtils';
|
||||
import '@testing-library/jest-dom';
|
||||
import { getConfig, setConfig } from '@edx/frontend-platform';
|
||||
import { CourseAuthoringProvider } from '@src/CourseAuthoringContext';
|
||||
import { RequestStatus } from '../data/constants';
|
||||
import { executeThunk } from '../utils';
|
||||
import { getCourseLaunchApiUrl, getCourseBestPracticesApiUrl } from './data/api';
|
||||
import { fetchCourseLaunchQuery, fetchCourseBestPracticesQuery } from './data/thunks';
|
||||
import {
|
||||
courseId,
|
||||
generateCourseLaunchData,
|
||||
@@ -20,7 +16,6 @@ import messages from './messages';
|
||||
import CourseChecklist from './index';
|
||||
|
||||
let axiosMock;
|
||||
let store;
|
||||
|
||||
const renderComponent = () => {
|
||||
render(
|
||||
@@ -33,22 +28,18 @@ const renderComponent = () => {
|
||||
const mockStore = async (status) => {
|
||||
axiosMock.onGet(getCourseLaunchApiUrl(courseId)).reply(status, generateCourseLaunchData());
|
||||
axiosMock.onGet(getCourseBestPracticesApiUrl(courseId)).reply(status, generateCourseBestPracticesData());
|
||||
|
||||
await executeThunk(fetchCourseLaunchQuery(courseId), store.dispatch);
|
||||
await executeThunk(fetchCourseBestPracticesQuery(courseId), store.dispatch);
|
||||
};
|
||||
|
||||
describe('CourseChecklistPage', () => {
|
||||
beforeEach(async () => {
|
||||
const mocks = initializeMocks();
|
||||
store = mocks.reduxStore;
|
||||
axiosMock = mocks.axiosMock;
|
||||
});
|
||||
describe('renders', () => {
|
||||
describe('if enable_quality prop is true', () => {
|
||||
it('two checklist components ', async () => {
|
||||
renderComponent();
|
||||
await mockStore(200);
|
||||
renderComponent();
|
||||
|
||||
expect(screen.getByText(messages.launchChecklistLabel.defaultMessage)).toBeVisible();
|
||||
|
||||
@@ -56,9 +47,9 @@ describe('CourseChecklistPage', () => {
|
||||
});
|
||||
|
||||
describe('an aria-live region with', () => {
|
||||
it('an aria-live region', () => {
|
||||
it('an aria-live region', async () => {
|
||||
renderComponent();
|
||||
const ariaLiveRegion = screen.getByRole('status');
|
||||
const ariaLiveRegion = await screen.findByRole('status');
|
||||
|
||||
expect(ariaLiveRegion).toBeDefined();
|
||||
|
||||
@@ -66,29 +57,17 @@ describe('CourseChecklistPage', () => {
|
||||
});
|
||||
|
||||
it('correct content when the launch checklist has loaded', async () => {
|
||||
renderComponent();
|
||||
await mockStore(404);
|
||||
await waitFor(() => {
|
||||
const { launchChecklistStatus } = store.getState().courseChecklist.loadingStatus;
|
||||
|
||||
expect(launchChecklistStatus).not.toEqual(RequestStatus.SUCCESSFUL);
|
||||
|
||||
expect(screen.getByText(messages.launchChecklistDoneLoadingLabel.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
renderComponent();
|
||||
expect(await screen.findByText(messages.launchChecklistDoneLoadingLabel.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('correct content when the best practices checklist is loading', async () => {
|
||||
renderComponent();
|
||||
await mockStore(404);
|
||||
await waitFor(() => {
|
||||
const { bestPracticeChecklistStatus } = store.getState().courseChecklist.loadingStatus;
|
||||
|
||||
expect(bestPracticeChecklistStatus).not.toEqual(RequestStatus.IN_PROGRESS);
|
||||
|
||||
expect(
|
||||
screen.getByText(messages.bestPracticesChecklistDoneLoadingLabel.defaultMessage),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
renderComponent();
|
||||
expect(
|
||||
await screen.findByText(messages.bestPracticesChecklistDoneLoadingLabel.defaultMessage),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -111,27 +90,15 @@ describe('CourseChecklistPage', () => {
|
||||
|
||||
describe('an aria-live region with', () => {
|
||||
it('correct content when the launch checklist has loaded', async () => {
|
||||
renderComponent();
|
||||
await mockStore(404);
|
||||
await waitFor(() => {
|
||||
const { launchChecklistStatus } = store.getState().courseChecklist.loadingStatus;
|
||||
|
||||
expect(launchChecklistStatus).not.toEqual(RequestStatus.SUCCESSFUL);
|
||||
|
||||
expect(screen.getByText(messages.launchChecklistDoneLoadingLabel.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
renderComponent();
|
||||
expect(await screen.findByText(messages.launchChecklistDoneLoadingLabel.defaultMessage)).toBeInTheDocument();
|
||||
});
|
||||
|
||||
it('correct content when the best practices checklist is loading', async () => {
|
||||
renderComponent();
|
||||
await mockStore(404);
|
||||
await waitFor(() => {
|
||||
const { bestPracticeChecklistStatus } = store.getState().courseChecklist.loadingStatus;
|
||||
|
||||
expect(bestPracticeChecklistStatus).not.toEqual(RequestStatus.IN_PROGRESS);
|
||||
|
||||
expect(screen.queryByText(messages.bestPracticesChecklistDoneLoadingLabel.defaultMessage)).toBeNull();
|
||||
});
|
||||
renderComponent();
|
||||
expect(screen.queryByText(messages.bestPracticesChecklistDoneLoadingLabel.defaultMessage)).toBeNull();
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -144,11 +111,7 @@ describe('CourseChecklistPage', () => {
|
||||
|
||||
renderComponent();
|
||||
|
||||
await waitFor(() => {
|
||||
const { launchChecklistStatus } = store.getState().courseChecklist.loadingStatus;
|
||||
expect(launchChecklistStatus).toEqual(RequestStatus.DENIED);
|
||||
expect(screen.getByRole('alert')).toBeInTheDocument();
|
||||
});
|
||||
expect(await screen.findByRole('alert')).toBeInTheDocument();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
||||
@@ -1,42 +1,33 @@
|
||||
import { useEffect } from 'react';
|
||||
import { getConfig } from '@edx/frontend-platform';
|
||||
import { useIntl } from '@edx/frontend-platform/i18n';
|
||||
import { Helmet } from 'react-helmet';
|
||||
import { useDispatch, useSelector } from 'react-redux';
|
||||
import { Container, Stack } from '@openedx/paragon';
|
||||
import { useCourseAuthoringContext } from '@src/CourseAuthoringContext';
|
||||
import { DeprecatedReduxState } from '@src/store';
|
||||
|
||||
import SubHeader from '../generic/sub-header/SubHeader';
|
||||
import messages from './messages';
|
||||
import AriaLiveRegion from './AriaLiveRegion';
|
||||
import { RequestStatus } from '../data/constants';
|
||||
import ChecklistSection from './ChecklistSection';
|
||||
import { fetchCourseLaunchQuery, fetchCourseBestPracticesQuery } from './data/thunks';
|
||||
import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
|
||||
import { useCourseBestPractices, useCourseLaunch } from './data/apiHooks';
|
||||
|
||||
const CourseChecklist = () => {
|
||||
const intl = useIntl();
|
||||
const dispatch = useDispatch();
|
||||
const { courseId, courseDetails } = useCourseAuthoringContext();
|
||||
const enableQuality = getConfig().ENABLE_CHECKLIST_QUALITY === 'true';
|
||||
|
||||
useEffect(() => {
|
||||
dispatch(fetchCourseLaunchQuery({ courseId }));
|
||||
dispatch(fetchCourseBestPracticesQuery({ courseId }));
|
||||
}, [courseId]);
|
||||
const {
|
||||
data: bestPracticeData,
|
||||
isPending: isPendingBestPacticeData,
|
||||
} = useCourseBestPractices({ courseId });
|
||||
|
||||
const {
|
||||
loadingStatus,
|
||||
launchData,
|
||||
bestPracticeData,
|
||||
} = useSelector((state: DeprecatedReduxState) => state.courseChecklist);
|
||||
data: launchData,
|
||||
isPending: isPendingLaunchData,
|
||||
failureReason: launchError,
|
||||
} = useCourseLaunch({ courseId });
|
||||
|
||||
const { bestPracticeChecklistLoadingStatus, launchChecklistLoadingStatus, launchChecklistStatus } = loadingStatus;
|
||||
|
||||
const isCourseLaunchChecklistLoading = bestPracticeChecklistLoadingStatus === RequestStatus.IN_PROGRESS;
|
||||
const isCourseBestPracticeChecklistLoading = launchChecklistLoadingStatus === RequestStatus.IN_PROGRESS;
|
||||
const isLoadingDenied = launchChecklistStatus === RequestStatus.DENIED;
|
||||
const isLoadingDenied = launchError?.response?.status === 403;
|
||||
|
||||
if (isLoadingDenied) {
|
||||
return (
|
||||
@@ -64,8 +55,8 @@ const CourseChecklist = () => {
|
||||
/>
|
||||
<AriaLiveRegion
|
||||
{...{
|
||||
isCourseLaunchChecklistLoading,
|
||||
isCourseBestPracticeChecklistLoading,
|
||||
isCourseLaunchChecklistLoading: isPendingLaunchData,
|
||||
isCourseBestPracticeChecklistLoading: isPendingBestPacticeData,
|
||||
enableQuality,
|
||||
}}
|
||||
/>
|
||||
@@ -75,7 +66,7 @@ const CourseChecklist = () => {
|
||||
dataHeading={intl.formatMessage(messages.launchChecklistLabel)}
|
||||
data={launchData}
|
||||
idPrefix="launchChecklist"
|
||||
isLoading={isCourseLaunchChecklistLoading}
|
||||
isLoading={isPendingLaunchData}
|
||||
/>
|
||||
{enableQuality && (
|
||||
<ChecklistSection
|
||||
@@ -83,7 +74,7 @@ const CourseChecklist = () => {
|
||||
dataHeading={intl.formatMessage(messages.bestPracticesChecklistLabel)}
|
||||
data={bestPracticeData}
|
||||
idPrefix="bestPracticesChecklist"
|
||||
isLoading={isCourseBestPracticeChecklistLoading}
|
||||
isLoading={isPendingBestPacticeData}
|
||||
/>
|
||||
)}
|
||||
</Stack>
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
// @ts-check
|
||||
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;
|
||||
|
||||
export const getCourseBestPracticesApiUrl = ({
|
||||
courseId,
|
||||
excludeGraded,
|
||||
all,
|
||||
}) => `${getApiBaseUrl()}/api/courses/v1/quality/${courseId}/?exclude_graded=${excludeGraded}&all=${all}`;
|
||||
|
||||
export const getCourseLaunchApiUrl = ({
|
||||
courseId,
|
||||
gradedOnly,
|
||||
validateOras,
|
||||
all,
|
||||
}) => `${getApiBaseUrl()}/api/courses/v1/validation/${courseId}/?graded_only=${gradedOnly}&validate_oras=${validateOras}&all=${all}`;
|
||||
|
||||
/**
|
||||
* Get course best practices.
|
||||
* @param {{courseId: string, excludeGraded: boolean, all: boolean}} options
|
||||
* @returns {Promise<{isSelfPaced: boolean, sections: any, subsection: any, units: any, videos: any }>}
|
||||
*/
|
||||
export async function getCourseBestPractices({
|
||||
courseId,
|
||||
excludeGraded,
|
||||
all,
|
||||
}) {
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.get(getCourseBestPracticesApiUrl({ courseId, excludeGraded, all }));
|
||||
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
|
||||
/** @typedef {object} courseLaunchData
|
||||
* @property {boolean} isSelfPaced
|
||||
* @property {object} dates
|
||||
* @property {object} assignments
|
||||
* @property {object} grades
|
||||
* @property {number} grades.sum_of_weights
|
||||
* @property {object} certificates
|
||||
* @property {object} updates
|
||||
* @property {object} proctoring
|
||||
*/
|
||||
|
||||
/**
|
||||
* Get course launch.
|
||||
* @param {{courseId: string, gradedOnly: boolean, validateOras: boolean, all: boolean}} options
|
||||
* @returns {Promise<courseLaunchData>}
|
||||
*/
|
||||
export async function getCourseLaunch({
|
||||
courseId,
|
||||
gradedOnly,
|
||||
validateOras,
|
||||
all,
|
||||
}) {
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.get(getCourseLaunchApiUrl({
|
||||
courseId, gradedOnly, validateOras, all,
|
||||
}));
|
||||
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
52
src/course-checklist/data/api.test.ts
Normal file
52
src/course-checklist/data/api.test.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
import { initializeMocks } from '@src/testUtils';
|
||||
import {
|
||||
CourseBestPracticesRequest,
|
||||
CourseLaunchRequest,
|
||||
getCourseBestPractices,
|
||||
getCourseBestPracticesApiUrl,
|
||||
getCourseLaunch,
|
||||
getCourseLaunchApiUrl,
|
||||
} from './api';
|
||||
|
||||
let axiosMock;
|
||||
|
||||
describe('course checklist data API', () => {
|
||||
beforeEach(() => {
|
||||
({ axiosMock } = initializeMocks());
|
||||
});
|
||||
|
||||
describe('getCourseBestPractices', () => {
|
||||
it('should fetch course best practices', async () => {
|
||||
const params: CourseBestPracticesRequest = {
|
||||
courseId: 'course-v1:edX+DemoX+Demo_Course',
|
||||
excludeGraded: true,
|
||||
all: true,
|
||||
};
|
||||
const url = getCourseBestPracticesApiUrl(params);
|
||||
axiosMock.onGet(url).reply(200, { is_self_paced: false });
|
||||
|
||||
const result = await getCourseBestPractices(params);
|
||||
|
||||
expect(axiosMock.history.get[0].url).toEqual(url);
|
||||
expect(result).toEqual({ isSelfPaced: false });
|
||||
});
|
||||
});
|
||||
|
||||
describe('getCourseLaunch', () => {
|
||||
it('should fetch course launch validation', async () => {
|
||||
const params: CourseLaunchRequest = {
|
||||
courseId: 'course-v1:edX+DemoX+Demo_Course',
|
||||
gradedOnly: true,
|
||||
validateOras: true,
|
||||
all: true,
|
||||
};
|
||||
const url = getCourseLaunchApiUrl(params);
|
||||
axiosMock.onGet(url).reply(200, { is_self_paced: false });
|
||||
|
||||
const result = await getCourseLaunch(params);
|
||||
|
||||
expect(axiosMock.history.get[0].url).toEqual(url);
|
||||
expect(result).toEqual({ isSelfPaced: false });
|
||||
});
|
||||
});
|
||||
});
|
||||
98
src/course-checklist/data/api.ts
Normal file
98
src/course-checklist/data/api.ts
Normal file
@@ -0,0 +1,98 @@
|
||||
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
|
||||
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
|
||||
|
||||
const getApiBaseUrl = () => getConfig().STUDIO_BASE_URL;
|
||||
|
||||
export interface CourseBestPracticesRequest {
|
||||
courseId: string;
|
||||
excludeGraded?: boolean;
|
||||
all?: boolean;
|
||||
}
|
||||
|
||||
export const getCourseBestPracticesApiUrl = ({
|
||||
courseId,
|
||||
excludeGraded = true,
|
||||
all = true,
|
||||
}: CourseBestPracticesRequest) => (
|
||||
`${getApiBaseUrl()}/api/courses/v1/quality/${courseId}/?exclude_graded=${excludeGraded}&all=${all}`
|
||||
);
|
||||
|
||||
export interface CourseLaunchRequest {
|
||||
courseId: string;
|
||||
gradedOnly?: boolean;
|
||||
validateOras?: boolean;
|
||||
all?: boolean;
|
||||
}
|
||||
|
||||
export const getCourseLaunchApiUrl = ({
|
||||
courseId,
|
||||
gradedOnly = true,
|
||||
validateOras = true,
|
||||
all = true,
|
||||
}: CourseLaunchRequest) => (
|
||||
`${getApiBaseUrl()}/api/courses/v1/validation/${courseId}/?graded_only=${gradedOnly}&validate_oras=${validateOras}&all=${all}`
|
||||
);
|
||||
|
||||
export interface CourseBestPractices {
|
||||
isSelfPaced: boolean;
|
||||
sections: Record<string, any>;
|
||||
subsection: Record<string, any>;
|
||||
units: Record<string, any>;
|
||||
videos: Record<string, any>;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get course best practices.
|
||||
*/
|
||||
export async function getCourseBestPractices({
|
||||
courseId,
|
||||
excludeGraded,
|
||||
all,
|
||||
}: CourseBestPracticesRequest): Promise<CourseBestPractices> {
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.get(getCourseBestPracticesApiUrl({ courseId, excludeGraded, all }));
|
||||
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
|
||||
export interface CourseLaunchData {
|
||||
isSelfPaced: boolean;
|
||||
dates: {
|
||||
hasEndDate: boolean;
|
||||
hasStartDate: boolean;
|
||||
};
|
||||
assignments: Record<string, any>;
|
||||
grades: {
|
||||
hasGradingPolicy: boolean;
|
||||
sumOfWeights: number;
|
||||
};
|
||||
certificates: {
|
||||
hasCertificate: boolean;
|
||||
isActivated: boolean;
|
||||
isEnabled: boolean;
|
||||
};
|
||||
updates: {
|
||||
hasUpdate: boolean;
|
||||
};
|
||||
proctoring: {
|
||||
hasProctoringEscalationEmail: boolean;
|
||||
needsProctoringEscalationEmail: boolean;
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Get course launch.
|
||||
*/
|
||||
export async function getCourseLaunch({
|
||||
courseId,
|
||||
gradedOnly,
|
||||
validateOras,
|
||||
all,
|
||||
}: CourseLaunchRequest): Promise<CourseLaunchData> {
|
||||
const { data } = await getAuthenticatedHttpClient()
|
||||
.get(getCourseLaunchApiUrl({
|
||||
courseId, gradedOnly, validateOras, all,
|
||||
}));
|
||||
|
||||
return camelCaseObject(data);
|
||||
}
|
||||
52
src/course-checklist/data/apiHooks.ts
Normal file
52
src/course-checklist/data/apiHooks.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/* eslint-disable import/no-extraneous-dependencies */
|
||||
import { AxiosError } from 'axios';
|
||||
import { useQuery } from '@tanstack/react-query';
|
||||
import {
|
||||
CourseBestPracticesRequest,
|
||||
CourseLaunchData,
|
||||
CourseLaunchRequest,
|
||||
getCourseBestPractices,
|
||||
getCourseLaunch,
|
||||
} from './api';
|
||||
|
||||
export const courseChecklistQueryKeys = {
|
||||
all: ['courseChecklist'],
|
||||
courseBestPractices: (params: CourseBestPracticesRequest) => [
|
||||
...courseChecklistQueryKeys.all,
|
||||
'bestPractices',
|
||||
params,
|
||||
],
|
||||
courseLaunch: (params: CourseLaunchRequest) => [
|
||||
...courseChecklistQueryKeys.all,
|
||||
'launch',
|
||||
params,
|
||||
],
|
||||
};
|
||||
|
||||
/**
|
||||
* Hook to fetch course best practices.
|
||||
*
|
||||
* It is necessary to update on each mount, because it is not known
|
||||
* for sure whether the checklist has been updated or not.
|
||||
*/
|
||||
export const useCourseBestPractices = (params: CourseBestPracticesRequest) => (
|
||||
useQuery({
|
||||
queryKey: courseChecklistQueryKeys.courseBestPractices(params),
|
||||
queryFn: () => getCourseBestPractices(params),
|
||||
refetchOnMount: 'always',
|
||||
})
|
||||
);
|
||||
|
||||
/**
|
||||
* Hook to fetch course launch validation.
|
||||
*
|
||||
* It is necessary to update on each mount, because it is not known
|
||||
* for sure whether the checklist has been updated or not.
|
||||
*/
|
||||
export const useCourseLaunch = (params: CourseLaunchRequest) => (
|
||||
useQuery<CourseLaunchData, AxiosError>({
|
||||
queryKey: courseChecklistQueryKeys.courseLaunch(params),
|
||||
queryFn: () => getCourseLaunch(params),
|
||||
refetchOnMount: 'always',
|
||||
})
|
||||
);
|
||||
@@ -1,41 +0,0 @@
|
||||
/* eslint-disable no-param-reassign */
|
||||
import { createSlice } from '@reduxjs/toolkit';
|
||||
|
||||
import { RequestStatus } from '../../data/constants';
|
||||
|
||||
const slice = createSlice({
|
||||
name: 'courseChecklist',
|
||||
initialState: {
|
||||
loadingStatus: {
|
||||
launchChecklistStatus: RequestStatus.IN_PROGRESS,
|
||||
bestPracticeChecklistStatus: RequestStatus.IN_PROGRESS,
|
||||
},
|
||||
launchData: {},
|
||||
bestPracticeData: {},
|
||||
},
|
||||
reducers: {
|
||||
fetchLaunchChecklistSuccess: (state, { payload }) => {
|
||||
state.launchData = payload.data;
|
||||
},
|
||||
updateLaunchChecklistStatus: (state, { payload }) => {
|
||||
state.loadingStatus.launchChecklistStatus = payload.status;
|
||||
},
|
||||
fetchBestPracticeChecklistSuccess: (state, { payload }) => {
|
||||
state.bestPracticeData = payload.data;
|
||||
},
|
||||
updateBestPracticeChecklisttStatus: (state, { payload }) => {
|
||||
state.loadingStatus.bestPracticeChecklistStatus = payload.status;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
export const {
|
||||
fetchLaunchChecklistSuccess,
|
||||
updateLaunchChecklistStatus,
|
||||
fetchBestPracticeChecklistSuccess,
|
||||
updateBestPracticeChecklisttStatus,
|
||||
} = slice.actions;
|
||||
|
||||
export const {
|
||||
reducer,
|
||||
} = slice;
|
||||
@@ -1,50 +0,0 @@
|
||||
import { RequestStatus } from '../../data/constants';
|
||||
import {
|
||||
getCourseBestPractices,
|
||||
getCourseLaunch,
|
||||
} from './api';
|
||||
import {
|
||||
fetchLaunchChecklistSuccess,
|
||||
updateLaunchChecklistStatus,
|
||||
fetchBestPracticeChecklistSuccess,
|
||||
updateBestPracticeChecklisttStatus,
|
||||
} from './slice';
|
||||
|
||||
export function fetchCourseLaunchQuery({
|
||||
courseId,
|
||||
gradedOnly = true,
|
||||
validateOras = true,
|
||||
all = true,
|
||||
}) {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const data = await getCourseLaunch({
|
||||
courseId, gradedOnly, validateOras, all,
|
||||
});
|
||||
dispatch(fetchLaunchChecklistSuccess({ data }));
|
||||
dispatch(updateLaunchChecklistStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
} catch (error) {
|
||||
if (error.response && error.response.status === 403) {
|
||||
dispatch(updateLaunchChecklistStatus({ status: RequestStatus.DENIED }));
|
||||
} else {
|
||||
dispatch(updateLaunchChecklistStatus({ status: RequestStatus.FAILED }));
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
export function fetchCourseBestPracticesQuery({
|
||||
courseId,
|
||||
excludeGraded = true,
|
||||
all = true,
|
||||
}) {
|
||||
return async (dispatch) => {
|
||||
try {
|
||||
const data = await getCourseBestPractices({ courseId, excludeGraded, all });
|
||||
dispatch(fetchBestPracticeChecklistSuccess({ data }));
|
||||
dispatch(updateBestPracticeChecklisttStatus({ status: RequestStatus.SUCCESSFUL }));
|
||||
} catch {
|
||||
dispatch(updateBestPracticeChecklisttStatus({ status: RequestStatus.FAILED }));
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -25,7 +25,6 @@ import { reducer as courseImportReducer } from './import-page/data/slice';
|
||||
import { reducer as videosReducer } from './files-and-videos/videos-page/data/slice';
|
||||
import { reducer as courseOutlineReducer } from './course-outline/data/slice';
|
||||
import { reducer as courseUnitReducer } from './course-unit/data/slice';
|
||||
import { reducer as courseChecklistReducer } from './course-checklist/data/slice';
|
||||
import { reducer as textbooksReducer } from './textbooks/data/slice';
|
||||
import { reducer as certificatesReducer } from './certificates/data/slice';
|
||||
import { reducer as groupConfigurationsReducer } from './group-configurations/data/slice';
|
||||
@@ -56,7 +55,6 @@ export interface DeprecatedReduxState {
|
||||
videos: Record<string, any>;
|
||||
courseOutline: Record<string, any>;
|
||||
courseUnit: Record<string, any>;
|
||||
courseChecklist: Record<string, any>;
|
||||
certificates: {
|
||||
loadingStatus: RequestStatusType;
|
||||
savingStatus: any;
|
||||
@@ -91,7 +89,6 @@ export default function initializeStore(preloadedState: Partial<DeprecatedReduxS
|
||||
videos: videosReducer,
|
||||
courseOutline: courseOutlineReducer,
|
||||
courseUnit: courseUnitReducer,
|
||||
courseChecklist: courseChecklistReducer,
|
||||
certificates: certificatesReducer,
|
||||
groupConfigurations: groupConfigurationsReducer,
|
||||
textbooks: textbooksReducer,
|
||||
|
||||
Reference in New Issue
Block a user