refactor: Migrate Help Urls from Redux store to React Query (#2714)

This commit is contained in:
Chris Chávez
2025-12-02 17:00:06 -05:00
committed by GitHub
parent e95758cc55
commit b76b003e01
10 changed files with 112 additions and 129 deletions

View File

@@ -1,35 +1,35 @@
module.exports = {
default: 'https://docs.openedx.org/en/latest/educators/index.html',
home: 'https://docs.openedx.org/en/latest/educators/navigation/content_creation_management.html',
develop_course: 'https://docs.openedx.org/en/latest/educators/references/course_content_development.html',
outline: 'https://docs.openedx.org/en/latest/educators/concepts/open_edx_platform/about_course_outline.html',
unit: 'https://docs.openedx.org/en/latest/educators/concepts/open_edx_platform/about_course_units.html',
visibility: 'https://docs.openedx.org/en/latest/educators/references/controlling_content_visibility.html',
updates: 'https://docs.openedx.org/en/latest/educators/concepts/communication/about_course_updates_handouts.html',
pages: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_custom_page.html',
files: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_course_files.html',
textbooks: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_textbooks.html',
schedule: 'https://docs.openedx.org/en/latest/educators/references/course_development/about_page.html',
grading: 'https://docs.openedx.org/en/latest/educators/concepts/grading/about_graded_subsections.html',
team_course: 'https://docs.openedx.org/en/latest/educators/references/course_development/course_team_roles.html#guide-to-course-team-roles',
team_library: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/library_access.html',
advanced: 'https://docs.openedx.org/en/latest/educators/navigation/advanced_features.html',
checklist: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html#quick-start-build-a-course',
import_library: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/export_import_library.html',
import_course: 'https://docs.openedx.org/en/latest/educators/how-tos/releasing-course/import_course.html',
export_library: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/export_import_library.html',
export_course: 'https://docs.openedx.org/en/latest/educators/how-tos/releasing-course/export_course.html',
welcome: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html#quick-start-build-a-course',
login: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html#quick-start-build-a-course',
register: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html#quick-start-build-a-course',
content_libraries: 'https://docs.openedx.org/en/latest/educators/navigation/content_creation_management.html#work-with-content-libraries',
content_groups: 'https://docs.openedx.org/en/latest/educators/navigation/advanced_features.html#manage-course-cohorts',
enrollment_tracks: 'https://docs.openedx.org/en/latest/educators/how-tos/advanced_features/create_content_for_specific_enrollment_tracks.html',
group_configurations: 'https://docs.openedx.org/en/latest/educators/concepts/advanced_features/about_group_configurations.html',
container: 'https://docs.openedx.org/en/latest/educators/references/course_development/parent_child_components.html',
video: 'https://docs.openedx.org/en/latest/educators/navigation/content_creation_management.html#manage-video-components',
certificates: 'https://docs.openedx.org/en/latest/educators/navigation/creating_course.html#set-up-course-certificates',
content_highlights: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_course_highlight_emails.html#set-course-section-highlights',
image_accessibility: 'https://docs.openedx.org/en/latest/educators/references/accessibility/accessibility_best_practices_checklist.html#use-best-practices-for-describing-images',
social_sharing: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/social_sharing.html',
checklist: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html#quick-start-build-a-course',
container: 'https://docs.openedx.org/en/latest/educators/references/course_development/parent_child_components.html',
contentGroups: 'https://docs.openedx.org/en/latest/educators/navigation/advanced_features.html#manage-course-cohorts',
contentHighlights: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_course_highlight_emails.html#set-course-section-highlights',
contentLibraries: 'https://docs.openedx.org/en/latest/educators/navigation/content_creation_management.html#work-with-content-libraries',
default: 'https://docs.openedx.org/en/latest/educators/index.html',
developCourse: 'https://docs.openedx.org/en/latest/educators/references/course_content_development.html',
enrollmentTracks: 'https://docs.openedx.org/en/latest/educators/how-tos/advanced_features/create_content_for_specific_enrollment_tracks.html',
exportCourse: 'https://docs.openedx.org/en/latest/educators/how-tos/releasing-course/export_course.html',
exportLibrary: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/export_import_library.html',
files: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_course_files.html',
grading: 'https://docs.openedx.org/en/latest/educators/concepts/grading/about_graded_subsections.html',
groupConfigurations: 'https://docs.openedx.org/en/latest/educators/concepts/advanced_features/about_group_configurations.html',
home: 'https://docs.openedx.org/en/latest/educators/navigation/content_creation_management.html',
imageAccessibility: 'https://docs.openedx.org/en/latest/educators/references/imageAccessibility/image_accessibility.html',
importCourse: 'https://docs.openedx.org/en/latest/educators/how-tos/releasing-course/import_course.html',
importLibrary: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/export_import_library.html',
login: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html#quick-start-build-a-course',
outline: 'https://docs.openedx.org/en/latest/educators/concepts/open_edx_platform/about_course_outline.html',
pages: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_custom_page.html',
register: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html#quick-start-build-a-course',
schedule: 'https://docs.openedx.org/en/latest/educators/references/course_development/about_page.html',
socialSharing: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/social_sharing.html',
teamCourse: 'https://docs.openedx.org/en/latest/educators/references/course_development/course_team_roles.html#guide-to-course-team-roles',
teamLibrary: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/library_access.html',
textbooks: 'https://docs.openedx.org/en/latest/educators/how-tos/course_development/manage_textbooks.html',
unit: 'https://docs.openedx.org/en/latest/educators/concepts/open_edx_platform/about_course_units.html',
updates: 'https://docs.openedx.org/en/latest/educators/concepts/communication/about_course_updates_handouts.html',
video: 'https://docs.openedx.org/en/latest/educators/navigation/content_creation_management.html#manage-video-components',
visibility: 'https://docs.openedx.org/en/latest/educators/references/controlling_content_visibility.html',
welcome: 'https://docs.openedx.org/en/latest/educators/quickstarts/build_a_course.html#quick-start-build-a-course',
};

View File

@@ -1,10 +0,0 @@
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
export const getHelpUrlsApiUrl = () => `${getConfig().STUDIO_BASE_URL}/api/contentstore/v1/help_urls`;
export async function getHelpUrls() {
const { data } = await getAuthenticatedHttpClient()
.get(getHelpUrlsApiUrl());
return camelCaseObject(data);
}

46
src/help-urls/data/api.ts Normal file
View File

@@ -0,0 +1,46 @@
import { camelCaseObject, getConfig } from '@edx/frontend-platform';
import { getAuthenticatedHttpClient } from '@edx/frontend-platform/auth';
export const getHelpUrlsApiUrl = () => `${getConfig().STUDIO_BASE_URL}/api/contentstore/v1/help_urls`;
export interface HelpUrls {
advanced: string;
certificates: string;
checklist: string;
container: string;
contentGroups: string;
contentHighlights: string;
contentLibraries: string;
default: string;
developCourse: string;
enrollmentTracks: string;
exportCourse: string;
exportLibrary: string;
files: string;
grading: string;
groupConfigurations: string;
home: string;
imageAccessibility: string;
importCourse: string;
importLibrary: string;
login: string;
outline: string;
pages: string;
register: string;
schedule: string;
socialSharing: string;
teamCourse: string;
teamLibrary: string;
textbooks: string;
unit: string;
updates: string;
video: string;
visibility: string;
welcome: string;
}
export async function getHelpUrls(): Promise<HelpUrls> {
const { data } = await getAuthenticatedHttpClient()
.get(getHelpUrlsApiUrl());
return camelCaseObject(data);
}

View File

@@ -0,0 +1,13 @@
import { useQuery } from '@tanstack/react-query';
import * as api from './api';
/**
* Hook to fetch all help urls
*/
export const useAllHelpUrls = () => (
useQuery({
queryKey: ['helpURLs'],
queryFn: api.getHelpUrls,
})
);

View File

@@ -1,13 +0,0 @@
export const selectHelpUrlsByNames = (names) => (state) => {
const urlsDictionary = {};
names.forEach(name => {
urlsDictionary[name] = state.helpUrls.pages[name] || null;
});
return urlsDictionary;
};
export const getPages = (state) => state.helpUrls.pages;
export const getLoadingHelpUrlsStatus = (state) => state.helpUrls.loadingHelpUrlsStatus;

View File

@@ -1,27 +0,0 @@
/* eslint-disable no-param-reassign */
import { createSlice } from '@reduxjs/toolkit';
const initialState = {
loadingHelpUrlsStatus: '',
pages: {},
};
const slice = createSlice({
name: 'helpUrls',
initialState,
reducers: {
updatePages: (state, { payload }) => {
state.pages = payload;
},
updateLoadingHelpUrlsStatus: (state, { payload }) => {
state.loadingHelpUrlsStatus = payload.status;
},
},
});
export const {
updatePages,
updateLoadingHelpUrlsStatus,
} = slice.actions;
export const { reducer } = slice;

View File

@@ -1,23 +0,0 @@
import { RequestStatus } from '../../data/constants';
import { getHelpUrls } from './api';
import { updateLoadingHelpUrlsStatus, updatePages } from './slice';
export function fetchHelpUrls() {
return async (dispatch) => {
dispatch(updateLoadingHelpUrlsStatus({ status: RequestStatus.IN_PROGRESS }));
try {
const urls = await getHelpUrls();
dispatch(updatePages(urls));
dispatch(updateLoadingHelpUrlsStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch {
dispatch(updateLoadingHelpUrlsStatus({ status: RequestStatus.FAILED }));
return false;
}
};
}

View File

@@ -1,22 +0,0 @@
import { useEffect } from 'react';
import { useDispatch, useSelector } from 'react-redux';
import { isEmpty } from 'lodash';
import { fetchHelpUrls } from './data/thunks';
import { getPages, selectHelpUrlsByNames } from './data/selectors';
const useHelpUrls = (tokenNames) => {
const dispatch = useDispatch();
const helpTokens = useSelector(selectHelpUrlsByNames(tokenNames));
const pages = useSelector(getPages);
useEffect(() => {
if (isEmpty(pages)) {
dispatch(fetchHelpUrls());
}
}, []);
return helpTokens;
};
export { useHelpUrls };

22
src/help-urls/hooks.tsx Normal file
View File

@@ -0,0 +1,22 @@
import { HelpUrls } from './data/api';
import { useAllHelpUrls } from './data/apiHooks';
const useHelpUrls = <T extends string[]>(tokenNames: T & (keyof HelpUrls)[]): {
[K in T[number]]?: K extends keyof HelpUrls ? string : null
} => {
const {
data: pages,
} = useAllHelpUrls();
const urlsDictionary = {};
if (pages) {
tokenNames.forEach(name => {
urlsDictionary[name] = pages[name] || null;
});
}
return urlsDictionary;
};
export { useHelpUrls };

View File

@@ -16,7 +16,6 @@ import { reducer as filesReducer } from './files-and-videos/files-page/data/slic
import { reducer as courseTeamReducer } from './course-team/data/slice';
import { reducer as CourseUpdatesReducer } from './course-updates/data/slice';
import { reducer as processingNotificationReducer } from './generic/processing-notification/data/slice';
import { reducer as helpUrlsReducer } from './help-urls/data/slice';
import { reducer as courseExportReducer } from './export-page/data/slice';
import { reducer as courseOptimizerReducer } from './optimizer-page/data/slice';
import { reducer as genericReducer } from './generic/data/slice';
@@ -50,7 +49,6 @@ export interface DeprecatedReduxState {
courseTeam: Record<string, any>;
courseUpdates: Record<string, any>;
processingNotification: Record<string, any>;
helpUrls: Record<string, any>;
courseExport: Record<string, any>;
courseOptimizer: Record<string, any>;
generic: Record<string, any>;
@@ -81,7 +79,6 @@ export default function initializeStore(preloadedState: Partial<DeprecatedReduxS
courseTeam: courseTeamReducer,
courseUpdates: CourseUpdatesReducer,
processingNotification: processingNotificationReducer,
helpUrls: helpUrlsReducer,
courseExport: courseExportReducer,
courseOptimizer: courseOptimizerReducer,
generic: genericReducer,