diff --git a/src/course-checklist/CourseChecklist.jsx b/src/course-checklist/CourseChecklist.jsx
index 5766bfe45e..b71817f168 100644
--- a/src/course-checklist/CourseChecklist.jsx
+++ b/src/course-checklist/CourseChecklist.jsx
@@ -13,6 +13,7 @@ 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 getUpdateLinks from './utils';
const CourseChecklist = ({
@@ -36,10 +37,19 @@ const CourseChecklist = ({
bestPracticeData,
} = useSelector(state => state.courseChecklist);
- const { bestPracticeChecklistLoadingStatus, launchChecklistLoadingStatus } = loadingStatus;
+ const { bestPracticeChecklistStatus, launchChecklistStatus } = loadingStatus;
- const isCourseLaunchChecklistLoading = bestPracticeChecklistLoadingStatus === RequestStatus.IN_PROGRESS;
- const isCourseBestPracticeChecklistLoading = launchChecklistLoadingStatus === RequestStatus.IN_PROGRESS;
+ const isCourseLaunchChecklistLoading = bestPracticeChecklistStatus === RequestStatus.IN_PROGRESS;
+ const isCourseBestPracticeChecklistLoading = launchChecklistStatus === RequestStatus.IN_PROGRESS;
+ const isLoadingDenied = launchChecklistStatus === RequestStatus.DENIED;
+
+ if (isLoadingDenied) {
+ return (
+
+
+
+ );
+ }
return (
<>
diff --git a/src/course-checklist/data/thunks.js b/src/course-checklist/data/thunks.js
index 20be7648a1..74547dab7b 100644
--- a/src/course-checklist/data/thunks.js
+++ b/src/course-checklist/data/thunks.js
@@ -24,7 +24,11 @@ export function fetchCourseLaunchQuery({
dispatch(fetchLaunchChecklistSuccess({ data }));
dispatch(updateLaunchChecklistStatus({ status: RequestStatus.SUCCESSFUL }));
} catch (error) {
- dispatch(updateLaunchChecklistStatus({ status: RequestStatus.FAILED }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateLaunchChecklistStatus({ status: RequestStatus.DENIED }));
+ } else {
+ dispatch(updateLaunchChecklistStatus({ status: RequestStatus.FAILED }));
+ }
}
};
}
diff --git a/src/course-outline/CourseOutline.jsx b/src/course-outline/CourseOutline.jsx
index e7468e68fb..95ba56acf0 100644
--- a/src/course-outline/CourseOutline.jsx
+++ b/src/course-outline/CourseOutline.jsx
@@ -68,6 +68,7 @@ const CourseOutline = ({ courseId }) => {
sectionsList,
isCustomRelativeDatesActive,
isLoading,
+ isLoadingDenied,
isReIndexShow,
showSuccessAlert,
isSectionsExpanded,
@@ -232,6 +233,27 @@ const CourseOutline = ({ courseId }) => {
);
}
+ if (isLoadingDenied) {
+ return (
+
+
+
+ );
+ }
+
return (
<>
diff --git a/src/course-outline/data/thunk.js b/src/course-outline/data/thunk.js
index 3508fde0bf..467629f8eb 100644
--- a/src/course-outline/data/thunk.js
+++ b/src/course-outline/data/thunk.js
@@ -96,10 +96,17 @@ export function fetchCourseOutlineIndexQuery(courseId) {
dispatch(updateOutlineIndexLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
} catch (error) {
- dispatch(updateOutlineIndexLoadingStatus({
- status: RequestStatus.FAILED,
- errors: getErrorDetails(error, false),
- }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateOutlineIndexLoadingStatus({
+ status: RequestStatus.DENIED,
+ errors: getErrorDetails(error, false),
+ }));
+ } else {
+ dispatch(updateOutlineIndexLoadingStatus({
+ status: RequestStatus.FAILED,
+ errors: getErrorDetails(error, false),
+ }));
+ }
}
};
}
diff --git a/src/course-outline/hooks.jsx b/src/course-outline/hooks.jsx
index f820f3d0ed..0ae09dea08 100644
--- a/src/course-outline/hooks.jsx
+++ b/src/course-outline/hooks.jsx
@@ -300,6 +300,7 @@ const useCourseOutline = ({ courseId }) => {
sectionsList,
isCustomRelativeDatesActive,
isLoading: outlineIndexLoadingStatus === RequestStatus.IN_PROGRESS,
+ isLoadingDenied: outlineIndexLoadingStatus === RequestStatus.DENIED,
isReIndexShow: Boolean(reindexLink),
showSuccessAlert,
isDisabledReindexButton,
diff --git a/src/course-outline/page-alerts/PageAlerts.jsx b/src/course-outline/page-alerts/PageAlerts.jsx
index 4b0eb0dc78..8fb981212b 100644
--- a/src/course-outline/page-alerts/PageAlerts.jsx
+++ b/src/course-outline/page-alerts/PageAlerts.jsx
@@ -341,7 +341,6 @@ const PageAlerts = ({
case API_ERROR_TYPES.serverError:
return {
key: k,
- desc: v.data,
title: intl.formatMessage(messages.serverErrorAlert, {
status: v.status,
}),
diff --git a/src/course-team/CourseTeam.jsx b/src/course-team/CourseTeam.jsx
index 10360460a3..29ad5b80f9 100644
--- a/src/course-team/CourseTeam.jsx
+++ b/src/course-team/CourseTeam.jsx
@@ -20,6 +20,7 @@ import CourseTeamMember from './course-team-member/CourseTeamMember';
import InfoModal from './info-modal/InfoModal';
import { useCourseTeam } from './hooks';
import getPageHeadTitle from '../generic/utils';
+import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
const CourseTeam = ({ courseId }) => {
const intl = useIntl();
@@ -35,6 +36,7 @@ const CourseTeam = ({ courseId }) => {
courseTeamUsers,
currentUserEmail,
isLoading,
+ isLoadingDenied,
isSingleAdmin,
isFormVisible,
isQueryPending,
@@ -55,6 +57,14 @@ const CourseTeam = ({ courseId }) => {
handleInternetConnectionFailed,
} = useCourseTeam({ intl, courseId });
+ if (isLoadingDenied) {
+ return (
+
+
+
+ );
+ }
+
if (isLoading) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>>;
diff --git a/src/course-team/data/thunk.js b/src/course-team/data/thunk.js
index 78012870f6..bfc3db193e 100644
--- a/src/course-team/data/thunk.js
+++ b/src/course-team/data/thunk.js
@@ -24,7 +24,11 @@ export function fetchCourseTeamQuery(courseId) {
dispatch(updateLoadingCourseTeamStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch (error) {
- dispatch(updateLoadingCourseTeamStatus({ status: RequestStatus.FAILED }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateLoadingCourseTeamStatus({ status: RequestStatus.DENIED }));
+ } else {
+ dispatch(updateLoadingCourseTeamStatus({ status: RequestStatus.FAILED }));
+ }
return false;
}
};
diff --git a/src/course-team/hooks.jsx b/src/course-team/hooks.jsx
index 1b3778fecf..7c6bc09a48 100644
--- a/src/course-team/hooks.jsx
+++ b/src/course-team/hooks.jsx
@@ -113,6 +113,7 @@ const useCourseTeam = ({ courseId }) => {
courseTeamUsers,
currentUserEmail,
isLoading: loadingCourseTeamStatus === RequestStatus.IN_PROGRESS,
+ isLoadingDenied: loadingCourseTeamStatus === RequestStatus.DENIED,
isSingleAdmin,
isFormVisible,
isAllowActions,
diff --git a/src/course-updates/CourseUpdates.jsx b/src/course-updates/CourseUpdates.jsx
index a6b7af677b..0d82f3bf8d 100644
--- a/src/course-updates/CourseUpdates.jsx
+++ b/src/course-updates/CourseUpdates.jsx
@@ -16,6 +16,7 @@ import { getProcessingNotification } from '../generic/processing-notification/da
import ProcessingNotification from '../generic/processing-notification';
import SubHeader from '../generic/sub-header/SubHeader';
import InternetConnectionAlert from '../generic/internet-connection-alert';
+import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
import { RequestStatus } from '../data/constants';
import CourseHandouts from './course-handouts/CourseHandouts';
import CourseUpdate from './course-update/CourseUpdate';
@@ -64,9 +65,18 @@ const CourseUpdates = ({ courseId }) => {
const errors = useSelector(getErrors);
const anyStatusFailed = matchesAnyStatus({ ...loadingStatuses, ...savingStatuses }, RequestStatus.FAILED);
+ const anyStatusDenied = matchesAnyStatus({ ...loadingStatuses, ...savingStatuses }, RequestStatus.DENIED);
const anyStatusInProgress = matchesAnyStatus({ ...loadingStatuses, ...savingStatuses }, RequestStatus.IN_PROGRESS);
const anyStatusPending = matchesAnyStatus({ ...loadingStatuses, ...savingStatuses }, RequestStatus.PENDING);
+ if (anyStatusDenied) {
+ return (
+
+
+
+ );
+ }
+
return (
<>
diff --git a/src/course-updates/data/thunk.js b/src/course-updates/data/thunk.js
index 88b3a0578a..225ca9e461 100644
--- a/src/course-updates/data/thunk.js
+++ b/src/course-updates/data/thunk.js
@@ -31,10 +31,17 @@ export function fetchCourseUpdatesQuery(courseId) {
error: { loadingUpdates: false },
}));
} catch (error) {
- dispatch(updateLoadingStatuses({
- status: { fetchCourseUpdatesQuery: RequestStatus.FAILED },
- error: { loadingUpdates: true },
- }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateLoadingStatuses({
+ status: { fetchCourseUpdatesQuery: RequestStatus.DENIED },
+ error: { loadingUpdates: true },
+ }));
+ } else {
+ dispatch(updateLoadingStatuses({
+ status: { fetchCourseUpdatesQuery: RequestStatus.FAILED },
+ error: { loadingUpdates: true },
+ }));
+ }
}
};
}
@@ -116,10 +123,17 @@ export function fetchCourseHandoutsQuery(courseId) {
error: { loadingHandouts: false },
}));
} catch (error) {
- dispatch(updateLoadingStatuses({
- status: { fetchCourseHandoutsQuery: RequestStatus.FAILED },
- error: { loadingHandouts: true },
- }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateLoadingStatuses({
+ status: { fetchCourseHandoutsQuery: RequestStatus.DENIED },
+ error: { loadingHandouts: true },
+ }));
+ } else {
+ dispatch(updateLoadingStatuses({
+ status: { fetchCourseHandoutsQuery: RequestStatus.FAILED },
+ error: { loadingHandouts: true },
+ }));
+ }
}
};
}
diff --git a/src/export-page/CourseExportPage.jsx b/src/export-page/CourseExportPage.jsx
index b332499732..36f96f2513 100644
--- a/src/export-page/CourseExportPage.jsx
+++ b/src/export-page/CourseExportPage.jsx
@@ -11,6 +11,7 @@ import { getConfig } from '@edx/frontend-platform';
import { Helmet } from 'react-helmet';
import InternetConnectionAlert from '../generic/internet-connection-alert';
+import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
import SubHeader from '../generic/sub-header/SubHeader';
import { RequestStatus } from '../data/constants';
import { useModel } from '../generic/model-store';
@@ -37,6 +38,7 @@ const CourseExportPage = ({ intl, courseId }) => {
const cookies = new Cookies();
const isShowExportButton = !exportTriggered || errorMessage || currentStage === EXPORT_STAGES.SUCCESS;
const anyRequestFailed = savingStatus === RequestStatus.FAILED || loadingStatus === RequestStatus.FAILED;
+ const isLoadingDenied = loadingStatus === RequestStatus.DENIED;
const anyRequestInProgress = savingStatus === RequestStatus.PENDING || loadingStatus === RequestStatus.IN_PROGRESS;
useEffect(() => {
@@ -48,6 +50,14 @@ const CourseExportPage = ({ intl, courseId }) => {
}
}, []);
+ if (isLoadingDenied) {
+ return (
+
+
+
+ );
+ }
+
return (
<>
diff --git a/src/export-page/data/thunks.js b/src/export-page/data/thunks.js
index f5fdcae07c..dd86ba84ef 100644
--- a/src/export-page/data/thunks.js
+++ b/src/export-page/data/thunks.js
@@ -89,7 +89,11 @@ export function fetchExportStatus(courseId) {
dispatch(updateLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch (error) {
- dispatch(updateLoadingStatus({ status: RequestStatus.FAILED }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateLoadingStatus({ status: RequestStatus.DENIED }));
+ } else {
+ dispatch(updateLoadingStatus({ courseId, status: RequestStatus.FAILED }));
+ }
return false;
}
};
diff --git a/src/grading-settings/GradingSettings.jsx b/src/grading-settings/GradingSettings.jsx
index 79234b603e..6c15fdbc9e 100644
--- a/src/grading-settings/GradingSettings.jsx
+++ b/src/grading-settings/GradingSettings.jsx
@@ -12,6 +12,7 @@ import AlertMessage from '../generic/alert-message';
import { RequestStatus } from '../data/constants';
import InternetConnectionAlert from '../generic/internet-connection-alert';
import SubHeader from '../generic/sub-header/SubHeader';
+import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
import SectionSubHeader from '../generic/section-sub-header';
import { STATEFUL_BUTTON_STATES } from '../constants';
import {
@@ -37,6 +38,7 @@ const GradingSettings = ({ intl, courseId }) => {
const courseAssignmentLists = useSelector(getCourseAssignmentLists);
const savingStatus = useSelector(getSavingStatus);
const loadingStatus = useSelector(getLoadingStatus);
+ const isLoadingDenied = loadingStatus === RequestStatus.DENIED;
const [showSuccessAlert, setShowSuccessAlert] = useState(false);
const dispatch = useDispatch();
const isLoading = loadingStatus === RequestStatus.IN_PROGRESS;
@@ -83,6 +85,14 @@ const GradingSettings = ({ intl, courseId }) => {
dispatch(fetchCourseSettingsQuery(courseId));
}, [courseId]);
+ if (isLoadingDenied) {
+ return (
+
+
+
+ );
+ }
+
if (isLoading) {
// eslint-disable-next-line react/jsx-no-useless-fragment
return <>>;
diff --git a/src/grading-settings/data/thunks.js b/src/grading-settings/data/thunks.js
index b7106eb390..39094ffe55 100644
--- a/src/grading-settings/data/thunks.js
+++ b/src/grading-settings/data/thunks.js
@@ -20,7 +20,11 @@ export function fetchGradingSettings(courseId) {
dispatch(fetchGradingSettingsSuccess(settingValues));
dispatch(updateLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
} catch (error) {
- dispatch(updateLoadingStatus({ status: RequestStatus.FAILED }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateLoadingStatus({ status: RequestStatus.DENIED }));
+ } else {
+ dispatch(updateLoadingStatus({ courseId, status: RequestStatus.FAILED }));
+ }
}
};
}
@@ -48,7 +52,11 @@ export function fetchCourseSettingsQuery(courseId) {
dispatch(updateLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
return true;
} catch (error) {
- dispatch(updateLoadingStatus({ status: RequestStatus.FAILED }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateLoadingStatus({ status: RequestStatus.DENIED }));
+ } else {
+ dispatch(updateLoadingStatus({ courseId, status: RequestStatus.FAILED }));
+ }
return false;
}
};
diff --git a/src/group-configurations/data/thunk.js b/src/group-configurations/data/thunk.js
index 16b961d96d..30ae354730 100644
--- a/src/group-configurations/data/thunk.js
+++ b/src/group-configurations/data/thunk.js
@@ -33,7 +33,11 @@ export function fetchGroupConfigurationsQuery(courseId) {
dispatch(fetchGroupConfigurations({ groupConfigurations }));
dispatch(updateLoadingStatus({ status: RequestStatus.SUCCESSFUL }));
} catch (error) {
- dispatch(updateLoadingStatus({ status: RequestStatus.FAILED }));
+ if (error.response && error.response.status === 403) {
+ dispatch(updateLoadingStatus({ status: RequestStatus.DENIED }));
+ } else {
+ dispatch(updateLoadingStatus({ courseId, status: RequestStatus.FAILED }));
+ }
}
};
}
diff --git a/src/group-configurations/hooks.jsx b/src/group-configurations/hooks.jsx
index 01187f1d2c..cbc3cb7b0e 100644
--- a/src/group-configurations/hooks.jsx
+++ b/src/group-configurations/hooks.jsx
@@ -85,6 +85,7 @@ const useGroupConfigurations = (courseId) => {
return {
isLoading: loadingStatus === RequestStatus.IN_PROGRESS,
+ isLoadingDenied: loadingStatus === RequestStatus.DENIED,
savingStatus,
contentGroupActions,
experimentConfigurationActions,
diff --git a/src/group-configurations/index.jsx b/src/group-configurations/index.jsx
index 49df275b86..cd4797f4fb 100644
--- a/src/group-configurations/index.jsx
+++ b/src/group-configurations/index.jsx
@@ -16,6 +16,7 @@ import ExperimentConfigurationsSection from './experiment-configurations-section
import EnrollmentTrackGroupsSection from './enrollment-track-groups-section';
import GroupConfigurationSidebar from './group-configuration-sidebar';
import { useGroupConfigurations } from './hooks';
+import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
const GroupConfigurations = ({ courseId }) => {
const { formatMessage } = useIntl();
@@ -34,6 +35,7 @@ const GroupConfigurations = ({ courseId }) => {
shouldShowExperimentGroups,
experimentGroupConfigurations,
},
+ isLoadingDenied,
} = useGroupConfigurations(courseId);
document.title = getPageHeadTitle(
@@ -41,6 +43,14 @@ const GroupConfigurations = ({ courseId }) => {
formatMessage(messages.headingTitle),
);
+ if (isLoadingDenied) {
+ return (
+
+
+
+ );
+ }
+
if (isLoading) {
return (
diff --git a/src/import-page/CourseImportPage.jsx b/src/import-page/CourseImportPage.jsx
index 368d44f741..2daea3fdb2 100644
--- a/src/import-page/CourseImportPage.jsx
+++ b/src/import-page/CourseImportPage.jsx
@@ -13,6 +13,7 @@ import SubHeader from '../generic/sub-header/SubHeader';
import InternetConnectionAlert from '../generic/internet-connection-alert';
import { RequestStatus } from '../data/constants';
import { useModel } from '../generic/model-store';
+import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
import {
updateFileName, updateImportTriggered, updateSavingStatus, updateSuccessDate,
} from './data/slice';
@@ -31,6 +32,7 @@ const CourseImportPage = ({ intl, courseId }) => {
const savingStatus = useSelector(getSavingStatus);
const loadingStatus = useSelector(getLoadingStatus);
const anyRequestFailed = savingStatus === RequestStatus.FAILED || loadingStatus === RequestStatus.FAILED;
+ const isLoadingDenied = loadingStatus === RequestStatus.DENIED;
const anyRequestInProgress = savingStatus === RequestStatus.PENDING || loadingStatus === RequestStatus.IN_PROGRESS;
useEffect(() => {
@@ -43,6 +45,14 @@ const CourseImportPage = ({ intl, courseId }) => {
}
}, []);
+ if (isLoadingDenied) {
+ return (
+
+
+
+ );
+ }
+
return (
<>
diff --git a/src/textbooks/Textbooks.jsx b/src/textbooks/Textbooks.jsx
index 905a1ec5f4..71b380224a 100644
--- a/src/textbooks/Textbooks.jsx
+++ b/src/textbooks/Textbooks.jsx
@@ -16,6 +16,7 @@ import { getProcessingNotification } from '../generic/processing-notification/da
import { useModel } from '../generic/model-store';
import { LoadingSpinner } from '../generic/Loading';
import SubHeader from '../generic/sub-header/SubHeader';
+import ConnectionErrorAlert from '../generic/ConnectionErrorAlert';
import ProcessingNotification from '../generic/processing-notification';
import EmptyPlaceholder from './empty-placeholder/EmptyPlaceholder';
import TextbookCard from './textbook-card/TextbooksCard';
@@ -33,6 +34,7 @@ const Textbooks = ({ courseId }) => {
const {
textbooks,
isLoading,
+ isLoadingFailed,
breadcrumbs,
errorMessage,
savingStatus,
@@ -50,6 +52,14 @@ const Textbooks = ({ courseId }) => {
title: processingNotificationTitle,
} = useSelector(getProcessingNotification);
+ if (isLoadingFailed) {
+ return (
+
+
+
+ );
+ }
+
if (isLoading) {
return (
diff --git a/src/textbooks/hooks.jsx b/src/textbooks/hooks.jsx
index 10f2d5b9be..b287fc284a 100644
--- a/src/textbooks/hooks.jsx
+++ b/src/textbooks/hooks.jsx
@@ -77,6 +77,7 @@ const useTextbooks = (courseId) => {
return {
isLoading: loadingStatus === RequestStatus.IN_PROGRESS,
+ isLoadingFailed: loadingStatus === RequestStatus.FAILED,
savingStatus,
errorMessage,
textbooks,