Skip to content

Commit

Permalink
fix(materials): display custom error page when file is missing in cou…
Browse files Browse the repository at this point in the history
…rse materials
  • Loading branch information
phungmanhcuong committed Oct 23, 2024
1 parent 80f1e6b commit f323b9b
Show file tree
Hide file tree
Showing 6 changed files with 131 additions and 11 deletions.
15 changes: 15 additions & 0 deletions client/app/bundles/course/material/folderLoader.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { LoaderFunction, redirect } from 'react-router-dom';
import { getIdFromUnknown } from 'utilities';

import CourseAPI from 'api/course';

const folderLoader: LoaderFunction = async ({ params }) => {
const folderId = getIdFromUnknown(params?.folderId);
if (!folderId) return redirect('/');

const { data } = await CourseAPI.folders.fetch(folderId);

return data;
};

export default folderLoader;
29 changes: 19 additions & 10 deletions client/app/bundles/course/material/folders/handles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,26 @@ const getFolderTitle = async (
courseUrl: string,
folderId: number,
): Promise<CrumbPath> => {
const { data } = await CourseAPI.folders.fetch(folderId);
try {
const { data } = await CourseAPI.folders.fetch(folderId);
const workbinUrl = `${courseUrl}/materials/folders/${data.breadcrumbs[0].id}`;

const workbinUrl = `${courseUrl}/materials/folders/${data.breadcrumbs[0].id}`;

return {
activePath: workbinUrl,
content: data.breadcrumbs.map((crumb) => ({
title: crumb.name,
url: `materials/folders/${crumb.id}`,
})),
};
return {
activePath: workbinUrl,
content: data.breadcrumbs.map((crumb) => ({
title: crumb.name,
url: `materials/folders/${crumb.id}`,
})),
};
} catch (error) {
return {
activePath: courseUrl,
content: {
title: 'Folder Not Found',
url: courseUrl,
},
};
}
};

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { ReactNode } from 'react';
import { Typography } from '@mui/material';

import Page from 'lib/components/core/layouts/Page';

interface BaseRetrievePageProps {
illustration: ReactNode;
title: string;
description: string;
children?: ReactNode;
}

const BaseRetrievePage = (
props: BaseRetrievePageProps,
): JSX.Element => (
<Page className="h-full m-auto flex flex-col items-center justify-center text-center">
{props.illustration}

<Typography className="mt-5" variant="h6">
{props.title}
</Typography>

<Typography
className="max-w-3xl mt-2"
color="text.secondary"
variant="body2"
>
{props.description}
</Typography>

{props.children}
</Page>
);

export default BaseRetrievePage;
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
import React from 'react';
import { defineMessages } from 'react-intl';
import { useParams } from 'react-router-dom';
import { Cancel, InsertDriveFileOutlined } from '@mui/icons-material';

import Link from 'lib/components/core/Link';
import useTranslation from 'lib/hooks/useTranslation';

import BaseRetrievePage from './BaseRetrievePage';

const translations = defineMessages({
problemRetrievingFolder: {
id: 'course.material.folders.ErrorRetrievingFolderPage.problemRetrievingFolder',
defaultMessage: 'Problem retrieving folder',
},
problemRetrievingFolderDescription: {
id: 'course.material.folders.ErrorRetrievingFolderPage.problemRetrievingFolderDescription',
defaultMessage:
"Either it no longer exists, you don't have the permission to access it, or something unexpected happened when we were trying to retrieve it.",
},
goToTheWorkbin: {
id: 'course.material.folders.ErrorRetrievingFolderPage.goToTheWorkbin',
defaultMessage: 'Go to the Workbin',
},
});

const ErrorRetrievingFolderPage = (): JSX.Element => {
const { t } = useTranslation();

const params = useParams();
const workbinURL = `/courses/${params.courseId}`;

return (
<BaseRetrievePage
description={t(translations.problemRetrievingFolderDescription)}
illustration={
<div className="relative">
<InsertDriveFileOutlined className="text-[6rem]" color="disabled" />

<Cancel
className="absolute bottom-0 -right-2 text-[4rem] bg-white rounded-full"
color="error"
/>
</div>
}
title={t(translations.problemRetrievingFolder)}
>
<Link className="mt-10" to={workbinURL}>
{t(translations.goToTheWorkbin)}
</Link>
</BaseRetrievePage>
);
};

export default ErrorRetrievingFolderPage;
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { FC, ReactElement, useEffect, useState } from 'react';
import { defineMessages } from 'react-intl';
import { useParams } from 'react-router-dom';
import {useLoaderData, useParams} from 'react-router-dom';

import EditButton from 'lib/components/core/buttons/EditButton';
import Page from 'lib/components/core/layouts/Page';
Expand Down Expand Up @@ -37,6 +37,8 @@ const FolderShow: FC = () => {
const { t } = useTranslation();
const dispatch = useAppDispatch();

const data = useLoaderData();
if (!data) throw new Error('No download data. This should never happen.');
// For new folder form dialog
const [isNewFolderOpen, setIsNewFolderOpen] = useState(false);
// For edit folder form dialog
Expand Down
4 changes: 4 additions & 0 deletions client/app/routers/AuthenticatedApp.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,7 @@ import LessonPlanShow from 'bundles/course/lesson-plan/pages/LessonPlanShow';
import LevelsIndex from 'bundles/course/level/pages/LevelsIndex';
import DownloadingFilePage from 'bundles/course/material/files/DownloadingFilePage';
import ErrorRetrievingFilePage from 'bundles/course/material/files/ErrorRetrievingFilePage';
import ErrorRetrievingFolderPage from 'bundles/course/material/folders/pages/ErrorRetrievingFolderPage';
import FolderShow from 'bundles/course/material/folders/pages/FolderShow';
import TimelineDesigner from 'bundles/course/reference-timelines/TimelineDesigner';
import ResponseEdit from 'bundles/course/survey/pages/ResponseEdit';
Expand Down Expand Up @@ -127,6 +128,7 @@ import {
forumTopicHandle,
} from 'course/forum/handles';
import { leaderboardHandle } from 'course/leaderboard/handles';
import folderLoader from 'course/material/folderLoader';
import { folderHandle } from 'course/material/folders/handles';
import materialLoader from 'course/material/materialLoader';
import { videoWatchHistoryHandle } from 'course/statistics/handles';
Expand Down Expand Up @@ -221,7 +223,9 @@ const authenticatedRouter: Translated<RouteObject[]> = (t) =>
children: [
{
index: true,
loader: folderLoader,
element: <FolderShow />,
errorElement: <ErrorRetrievingFolderPage />,
},
{
path: 'files/:materialId',
Expand Down

0 comments on commit f323b9b

Please sign in to comment.