Skip to content

Commit 848ee58

Browse files
committed
feat: refactor course details management by removing deprecated actions and integrating new API hooks
1 parent dc5c08d commit 848ee58

8 files changed

Lines changed: 46 additions & 65 deletions

File tree

src/editors/sharedComponents/TinyMceWidget/hooks.js

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,6 @@ import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
99
import { getLocale, isRtl } from '@edx/frontend-platform/i18n';
1010
import { a11ycheckerCss } from 'frontend-components-tinymce-advanced-plugins';
1111
import { isEmpty } from 'lodash';
12-
import { updateCourseDetailsOverview } from '../../../schedule-and-details/data/slice';
1312
import tinyMCEStyles from '../../data/constants/tinyMCEStyles';
1413
import { StrictDict } from '../../utils';
1514
import pluginConfig from './pluginConfig';
@@ -204,7 +203,6 @@ export const setupCustomBehavior = ({
204203
setImage,
205204
lmsEndpointUrl,
206205
learningContextId,
207-
dispatch,
208206
}) => (editor) => {
209207
// image upload button
210208
editor.ui.registry.addButton(tinyMCE.buttons.imageUploadButton, {
@@ -291,8 +289,7 @@ export const setupCustomBehavior = ({
291289
initialContent,
292290
learningContextId,
293291
});
294-
// Update initialValue so the change is not taken as a user action
295-
if (newContent) { dispatch(updateCourseDetailsOverview(newContent)); }
292+
if (newContent) { editor.setContent(newContent); }
296293
}
297294
if (e.command === 'RemoveFormat') {
298295
editor.formatter.remove('blockquote');
@@ -323,7 +320,6 @@ export const editorConfig = ({
323320
learningContextId,
324321
staticRootUrl,
325322
enableImageUpload,
326-
dispatch,
327323
}) => {
328324
const lmsEndpointUrl = getConfig().LMS_BASE_URL;
329325
const studioEndpointUrl = getConfig().STUDIO_BASE_URL;
@@ -369,7 +365,6 @@ export const editorConfig = ({
369365
content,
370366
images,
371367
learningContextId,
372-
dispatch,
373368
}),
374369
quickbars_insert_toolbar: quickbarsInsertToolbar,
375370
quickbars_selection_toolbar: quickbarsSelectionToolbar,
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
import { useQuery, useQueryClient } from '@tanstack/react-query';
2+
3+
import { getCourseDetails } from './api';
4+
5+
/**
6+
* Get the details of a course.
7+
*/
8+
export const useGetCourseDetails = (courseId?: string) => {
9+
const queryClient = useQueryClient();
10+
11+
const {
12+
data, isLoading, isError, refetch,
13+
} = useQuery({
14+
queryKey: ['courseDetails', courseId],
15+
queryFn: () => getCourseDetails(courseId),
16+
refetchOnWindowFocus: false,
17+
});
18+
let globalDefaults: { [key: string]: any } | undefined;
19+
if (data === undefined && courseId) {
20+
// If course-specific waffle flags were requested, first default to the
21+
// global (studio-wide) flags until we've loaded the course-specific ones.
22+
globalDefaults = queryClient.getQueryData(['courseDetails', undefined]);
23+
}
24+
return {
25+
...globalDefaults,
26+
...data,
27+
id: courseId,
28+
isLoading,
29+
isError,
30+
refetch,
31+
};
32+
};
Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,3 @@
1-
export const getLoadingDetailsStatus = (state) => state.scheduleAndDetails.loadingDetailsStatus;
21
export const getLoadingSettingsStatus = (state) => state.scheduleAndDetails.loadingSettingsStatus;
32
export const getSavingStatus = (state) => state.scheduleAndDetails.savingStatus;
4-
export const getCourseDetails = state => state.scheduleAndDetails.courseDetails;
53
export const getCourseSettings = (state) => state.scheduleAndDetails.courseSettings;

src/schedule-and-details/data/slice.js

Lines changed: 0 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -6,45 +6,27 @@ import { RequestStatus } from '../../data/constants';
66
const slice = createSlice({
77
name: 'scheduleAndDetails',
88
initialState: {
9-
loadingDetailsStatus: RequestStatus.IN_PROGRESS,
109
loadingSettingsStatus: RequestStatus.IN_PROGRESS,
1110
savingStatus: '',
12-
courseDetails: {},
1311
courseSettings: {},
1412
},
1513
reducers: {
16-
updateLoadingDetailsStatus: (state, { payload }) => {
17-
state.loadingDetailsStatus = payload.status;
18-
},
1914
updateLoadingSettingsStatus: (state, { payload }) => {
2015
state.loadingSettingsStatus = payload.status;
2116
},
2217
updateSavingStatus: (state, { payload }) => {
2318
state.savingStatus = payload.status;
2419
},
25-
updateCourseDetailsSuccess: (state, { payload }) => {
26-
Object.assign(state.courseDetails, payload);
27-
},
28-
fetchCourseDetailsSuccess: (state, { payload }) => {
29-
Object.assign(state.courseDetails, payload);
30-
},
3120
fetchCourseSettingsSuccess: (state, { payload }) => {
3221
Object.assign(state.courseSettings, payload);
3322
},
34-
updateCourseDetailsOverview: (state, { payload }) => {
35-
state.courseDetails.overview = payload;
36-
},
3723
},
3824
});
3925

4026
export const {
4127
updateSavingStatus,
42-
updateLoadingDetailsStatus,
4328
updateLoadingSettingsStatus,
44-
updateCourseDetailsSuccess,
45-
fetchCourseDetailsSuccess,
4629
fetchCourseSettingsSuccess,
47-
updateCourseDetailsOverview,
4830
} = slice.actions;
4931

5032
export const {

src/schedule-and-details/data/thunks.js

Lines changed: 1 addition & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,44 +1,21 @@
11
import { RequestStatus } from '../../data/constants';
22
import {
3-
getCourseDetails,
43
updateCourseDetails,
54
getCourseSettings,
65
} from './api';
76
import {
87
updateSavingStatus,
9-
updateLoadingDetailsStatus,
108
updateLoadingSettingsStatus,
11-
fetchCourseDetailsSuccess,
12-
updateCourseDetailsSuccess,
139
fetchCourseSettingsSuccess,
1410
} from './slice';
1511

16-
export function fetchCourseDetailsQuery(courseId) {
17-
return async (dispatch) => {
18-
dispatch(updateLoadingDetailsStatus({ status: RequestStatus.IN_PROGRESS }));
19-
20-
try {
21-
const detailsValues = await getCourseDetails(courseId);
22-
dispatch(fetchCourseDetailsSuccess(detailsValues));
23-
dispatch(updateLoadingDetailsStatus({ status: RequestStatus.SUCCESSFUL }));
24-
} catch (error) {
25-
if (error.response && error.response.status === 403) {
26-
dispatch(updateLoadingDetailsStatus({ courseId, status: RequestStatus.DENIED }));
27-
} else {
28-
dispatch(updateLoadingDetailsStatus({ status: RequestStatus.FAILED }));
29-
}
30-
}
31-
};
32-
}
33-
3412
export function updateCourseDetailsQuery(courseId, details) {
3513
return async (dispatch) => {
3614
dispatch(updateSavingStatus({ status: RequestStatus.IN_PROGRESS }));
3715

3816
try {
39-
const detailsValues = await updateCourseDetails(courseId, details);
17+
await updateCourseDetails(courseId, details);
4018
dispatch(updateSavingStatus({ status: RequestStatus.SUCCESSFUL }));
41-
dispatch(updateCourseDetailsSuccess(detailsValues));
4219
return true;
4320
} catch (error) {
4421
dispatch(updateSavingStatus({ status: RequestStatus.FAILED }));

src/schedule-and-details/hooks.jsx

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,30 +3,28 @@ import { useDispatch, useSelector } from 'react-redux';
33
import { useIntl } from '@edx/frontend-platform/i18n';
44

55
import { RequestStatus } from '../data/constants';
6-
import { getLoadingDetailsStatus, getLoadingSettingsStatus, getSavingStatus } from './data/selectors';
6+
import { getLoadingSettingsStatus, getSavingStatus } from './data/selectors';
77
import { validateScheduleAndDetails, updateWithDefaultValues } from './utils';
88

99
const useLoadValuesPrompt = (
1010
courseId,
11-
fetchCourseDetailsQuery,
1211
fetchCourseSettingsQuery,
12+
courseDetailsError,
1313
) => {
1414
const dispatch = useDispatch();
15-
const loadingDetailsStatus = useSelector(getLoadingDetailsStatus);
1615
const loadingSettingsStatus = useSelector(getLoadingSettingsStatus);
1716
const [showLoadFailedAlert, setShowLoadFailedAlert] = useState(false);
1817

1918
useEffect(() => {
20-
dispatch(fetchCourseDetailsQuery(courseId));
2119
dispatch(fetchCourseSettingsQuery(courseId));
2220
}, [courseId]);
2321

2422
useEffect(() => {
25-
if (loadingDetailsStatus === RequestStatus.FAILED || loadingSettingsStatus === RequestStatus.FAILED) {
23+
if (courseDetailsError || loadingSettingsStatus === RequestStatus.FAILED) {
2624
setShowLoadFailedAlert(true);
2725
window.scrollTo({ top: 0, behavior: 'smooth' });
2826
}
29-
}, [loadingDetailsStatus, loadingSettingsStatus]);
27+
}, [courseDetailsError, loadingSettingsStatus]);
3028

3129
return {
3230
showLoadFailedAlert,
@@ -54,7 +52,7 @@ const useSaveValuesPrompt = (
5452
if (!isQueryPending && !isEditableState) {
5553
setEditedValues(initialEditedData);
5654
}
57-
}, [initialEditedData]);
55+
}, [initialEditedData.isLoading]);
5856

5957
useEffect(() => {
6058
const errors = validateScheduleAndDetails(editedValues, canShowCertificateAvailableDateField, intl);
@@ -115,6 +113,8 @@ const useSaveValuesPrompt = (
115113
if (!isEditableState) {
116114
setShowModifiedAlert(false);
117115
}
116+
// Refresh course data after successful save
117+
initialEditedData.refetch();
118118
} else if (savingStatus === RequestStatus.FAILED) {
119119
setIsQueryPending(false);
120120
setShowSuccessfulAlert(false);

src/schedule-and-details/index.jsx

Lines changed: 5 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ import { useIntl } from '@edx/frontend-platform/i18n';
1313

1414
import Placeholder from '../editors/Placeholder';
1515
import { RequestStatus } from '../data/constants';
16+
import { useGetCourseDetails } from './data/apiHooks';
1617
import { useModel } from '../generic/model-store';
1718
import AlertMessage from '../generic/alert-message';
1819
import InternetConnectionAlert from '../generic/internet-connection-alert';
@@ -21,13 +22,10 @@ import getPageHeadTitle from '../generic/utils';
2122
import { useScrollToHashElement } from '../hooks';
2223
import {
2324
fetchCourseSettingsQuery,
24-
fetchCourseDetailsQuery,
2525
updateCourseDetailsQuery,
2626
} from './data/thunks';
2727
import {
2828
getCourseSettings,
29-
getCourseDetails,
30-
getLoadingDetailsStatus,
3129
getLoadingSettingsStatus,
3230
} from './data/selectors';
3331
import BasicSection from './basic-section';
@@ -46,11 +44,10 @@ import { useLoadValuesPrompt, useSaveValuesPrompt } from './hooks';
4644

4745
const ScheduleAndDetails = ({ courseId }) => {
4846
const intl = useIntl();
47+
const courseDetails = useGetCourseDetails(courseId);
4948
const courseSettings = useSelector(getCourseSettings);
50-
const courseDetails = useSelector(getCourseDetails);
51-
const loadingDetailsStatus = useSelector(getLoadingDetailsStatus);
5249
const loadingSettingsStatus = useSelector(getLoadingSettingsStatus);
53-
const isLoading = loadingDetailsStatus === RequestStatus.IN_PROGRESS
50+
const isLoading = courseDetails.isLoading
5451
|| loadingSettingsStatus === RequestStatus.IN_PROGRESS;
5552

5653
const course = useModel('courseDetails', courseId);
@@ -82,8 +79,8 @@ const ScheduleAndDetails = ({ courseId }) => {
8279
showLoadFailedAlert,
8380
} = useLoadValuesPrompt(
8481
courseId,
85-
fetchCourseDetailsQuery,
8682
fetchCourseSettingsQuery,
83+
courseDetails.isError,
8784
);
8885

8986
const {
@@ -146,7 +143,7 @@ const ScheduleAndDetails = ({ courseId }) => {
146143
return <></>;
147144
}
148145

149-
if (loadingDetailsStatus === RequestStatus.DENIED || loadingSettingsStatus === RequestStatus.DENIED) {
146+
if (courseDetails.isError || loadingSettingsStatus === RequestStatus.DENIED) {
150147
return (
151148
<div className="row justify-content-center m-6">
152149
<Placeholder />

0 commit comments

Comments
 (0)