From 108e7e3b1c83daaa71af853c93b9d1721553a4ff Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EA=B9=80=EA=B7=9C=ED=9A=8C?=
<48755156+KimKyuHoi@users.noreply.github.com>
Date: Mon, 5 Aug 2024 21:20:57 +0900
Subject: [PATCH] Implement navigate to 404 error page when accessing
non-existent URL in workspaceSlug (#244)
* Add 404 NotFound Page
* Add query error exception handling condition
* Navigate to page 404 according to error conditions
* Fix to Specify specific error code
* Add error and loading conditions
* Move to Global Logic of axios Error status
* Remove Logic of Axios Error
* Comment to change it to navigate
* Move File hooks to utils
* Fix: CustomNavigate
* Revert "Fix: CustomNavigate"
This reverts commit 3a48e1e9798d406295e24553cc5fe622fcca2201.
* Fix customNavigate to window.location.href
* Remove customNavigate
* Rebase: Code Conflict
---
frontend/src/App.tsx | 11 ++++-
frontend/src/pages/error/index.tsx | 42 +++++++++++++++++++
frontend/src/pages/workspace/Index.tsx | 13 +++++-
.../src/pages/workspace/document/Index.tsx | 20 +++++++--
frontend/src/routes.tsx | 6 +++
frontend/src/utils/axios.default.ts | 9 ++++
6 files changed, 95 insertions(+), 6 deletions(-)
create mode 100644 frontend/src/pages/error/index.tsx
create mode 100644 frontend/src/utils/axios.default.ts
diff --git a/frontend/src/App.tsx b/frontend/src/App.tsx
index 7f6727ee..c8e6c966 100755
--- a/frontend/src/App.tsx
+++ b/frontend/src/App.tsx
@@ -22,6 +22,7 @@ import AuthProvider from "./providers/AuthProvider";
import { useErrorHandler } from "./hooks/useErrorHandler";
import * as Sentry from "@sentry/react";
import { useGetSettingsQuery } from "./hooks/api/settings";
+import { isAxios404Error, isAxios500Error } from "./utils/axios.default";
if (import.meta.env.PROD) {
Sentry.init({
@@ -76,7 +77,15 @@ function App() {
const queryClient = useMemo(() => {
return new QueryClient({
queryCache: new QueryCache({
- onError: handleError,
+ onError: (error) => {
+ if (isAxios404Error(error)) {
+ window.location.href = "/404";
+ } else if (isAxios500Error(error)) {
+ window.location.href = "/404";
+ } else {
+ handleError(error);
+ }
+ },
}),
defaultOptions: {
mutations: {
diff --git a/frontend/src/pages/error/index.tsx b/frontend/src/pages/error/index.tsx
new file mode 100644
index 00000000..defc3e72
--- /dev/null
+++ b/frontend/src/pages/error/index.tsx
@@ -0,0 +1,42 @@
+import { Box, Typography, Button } from "@mui/material";
+import { useNavigate } from "react-router-dom";
+
+const NotFound = () => {
+ const navigate = useNavigate();
+
+ const handleGoHome = () => {
+ navigate("/");
+ };
+
+ return (
+
+
+ 404
+
+
+ Page Not Found
+
+
+ The page you are looking for does not exist.
+
+
+
+ );
+};
+
+export default NotFound;
diff --git a/frontend/src/pages/workspace/Index.tsx b/frontend/src/pages/workspace/Index.tsx
index 62d9d8de..69e2b015 100644
--- a/frontend/src/pages/workspace/Index.tsx
+++ b/frontend/src/pages/workspace/Index.tsx
@@ -4,7 +4,7 @@ import {
useGetWorkspaceDocumentListQuery,
} from "../../hooks/api/workspaceDocument";
import { useGetWorkspaceQuery } from "../../hooks/api/workspace";
-import { Box, Button, CircularProgress, Grid, Stack, Typography } from "@mui/material";
+import { Backdrop, Box, Button, CircularProgress, Grid, Stack, Typography } from "@mui/material";
import DocumentCard from "../../components/cards/DocumentCard";
import { useMemo, useState } from "react";
import { Document } from "../../hooks/api/types/document.d";
@@ -15,7 +15,8 @@ import AddIcon from "@mui/icons-material/Add";
function WorkspaceIndex() {
const params = useParams();
const navigate = useNavigate();
- const { data: workspace } = useGetWorkspaceQuery(params.workspaceSlug);
+ const { data: workspace, isLoading } = useGetWorkspaceQuery(params.workspaceSlug);
+
const {
data: documentPageList,
fetchNextPage,
@@ -31,6 +32,14 @@ function WorkspaceIndex() {
);
}, [documentPageList?.pages]);
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
const handleCreateDocumentModalOpen = () => {
setCreateDocumentModalOpen((prev) => !prev);
};
diff --git a/frontend/src/pages/workspace/document/Index.tsx b/frontend/src/pages/workspace/document/Index.tsx
index 6ee3ca29..2a3fa823 100644
--- a/frontend/src/pages/workspace/document/Index.tsx
+++ b/frontend/src/pages/workspace/document/Index.tsx
@@ -1,7 +1,7 @@
import { useEffect } from "react";
import { setClient, setDoc } from "../../../store/editorSlice";
import { useDispatch, useSelector } from "react-redux";
-import { Box } from "@mui/material";
+import { Backdrop, Box, CircularProgress } from "@mui/material";
import { useParams } from "react-router-dom";
import { selectUser } from "../../../store/userSlice";
import { useGetDocumentQuery } from "../../../hooks/api/workspaceDocument";
@@ -14,10 +14,16 @@ import { selectSetting } from "../../../store/settingSlice";
function DocumentIndex() {
const dispatch = useDispatch();
const params = useParams();
+
const userStore = useSelector(selectUser);
const settingStore = useSelector(selectSetting);
- const { data: workspace } = useGetWorkspaceQuery(params.workspaceSlug);
- const { data: document } = useGetDocumentQuery(workspace?.id, params.documentId);
+ const { data: workspace, isLoading: isWorkspaceLoading } = useGetWorkspaceQuery(
+ params.workspaceSlug
+ );
+ const { data: document, isLoading: isDocumentLoading } = useGetDocumentQuery(
+ workspace?.id,
+ params.documentId
+ );
const { doc, client } = useYorkieDocument(document?.yorkieDocumentId, userStore.data?.nickname);
useEffect(() => {
@@ -32,6 +38,14 @@ function DocumentIndex() {
};
}, [dispatch, client, doc]);
+ if (isDocumentLoading || isWorkspaceLoading) {
+ return (
+
+
+
+ );
+ }
+
return (
diff --git a/frontend/src/routes.tsx b/frontend/src/routes.tsx
index ff607bce..79dd1d85 100644
--- a/frontend/src/routes.tsx
+++ b/frontend/src/routes.tsx
@@ -11,6 +11,7 @@ import DocumentIndex from "./pages/workspace/document/Index";
import DocumentShareIndex from "./pages/workspace/document/share/Index";
import JoinIndex from "./pages/workspace/join/Index";
import MemberIndex from "./pages/workspace/member/Index";
+import NotFound from "./pages/error";
import ProfileIndex from "./pages/settings/profile/Index";
import SettingLayout from "./components/layouts/SettingLayout";
@@ -85,6 +86,11 @@ const codePairRoutes: Array = [
accessType: AccessType.PRIVATE,
element: ,
},
+ {
+ path: "/404",
+ accessType: AccessType.PUBLIC,
+ element: ,
+ },
{
path: "settings/profile",
accessType: AccessType.PRIVATE,
diff --git a/frontend/src/utils/axios.default.ts b/frontend/src/utils/axios.default.ts
new file mode 100644
index 00000000..acf4bc74
--- /dev/null
+++ b/frontend/src/utils/axios.default.ts
@@ -0,0 +1,9 @@
+import { AxiosError } from "axios";
+
+export const isAxios404Error = (error: unknown): boolean => {
+ return error instanceof AxiosError && error.response?.status === 404;
+};
+
+export const isAxios500Error = (error: unknown): boolean => {
+ return error instanceof AxiosError && error.response?.status === 500;
+};