diff --git a/apps/web/src/pages/404.tsx b/apps/web/src/pages/404.tsx new file mode 100644 index 00000000..3f1069ae --- /dev/null +++ b/apps/web/src/pages/404.tsx @@ -0,0 +1,55 @@ +import Link from "next/link"; +import { t } from "@lingui/core/macro"; +import { Trans } from "@lingui/react/macro"; + +import { PageHead } from "~/components/PageHead"; +import PatternedBackground from "~/components/PatternedBackground"; + +export default function NotFoundPage() { + return ( + <> + +
+
+
+ +

+ kan.bn +

+ +

+ 404 +

+

+ Page not found +

+
+
+

+ + The page you're looking for doesn't exist or has been moved. + +

+
+ + Go to homepage + + + Go to boards + +
+
+
+
+ +
+
+ + ); +} diff --git a/apps/web/src/views/board/index.tsx b/apps/web/src/views/board/index.tsx index 6074a56e..d0d400a6 100644 --- a/apps/web/src/views/board/index.tsx +++ b/apps/web/src/views/board/index.tsx @@ -125,11 +125,21 @@ export default function BoardPage({ isTemplate }: { isTemplate?: boolean }) { data: boardData, isSuccess, isLoading: isQueryLoading, + error, } = api.board.byId.useQuery(queryParams, { enabled: !!boardId, placeholderData: keepPreviousData, }); + // Redirect to 404 if board doesn't exist + useEffect(() => { + if (router.isReady && boardId && !isQueryLoading) { + if (error?.data?.code === "NOT_FOUND" || (!boardData && !isQueryLoading)) { + router.replace("/404"); + } + } + }, [router, boardId, isQueryLoading, error, boardData]); + const refetchBoard = async () => { if (boardId) await utils.board.byId.refetch({ boardPublicId: boardId }); }; diff --git a/apps/web/src/views/card/index.tsx b/apps/web/src/views/card/index.tsx index 49a86cff..7eacfc43 100644 --- a/apps/web/src/views/card/index.tsx +++ b/apps/web/src/views/card/index.tsx @@ -185,11 +185,20 @@ export default function CardPage({ isTemplate }: { isTemplate?: boolean }) { ? router.query.cardId[0] : router.query.cardId; - const { data: card, isLoading } = api.card.byId.useQuery( + const { data: card, isLoading, error } = api.card.byId.useQuery( { cardPublicId: cardId ?? "" }, { enabled: !!cardId && cardId.length >= 12 }, ); + // Redirect to 404 if card doesn't exist + useEffect(() => { + if (router.isReady && cardId && !isLoading) { + if (error?.data?.code === "NOT_FOUND" || (!card && !isLoading)) { + router.replace("/404"); + } + } + }, [router, cardId, isLoading, error, card]); + const isCreator = card?.createdBy && session?.user.id === card.createdBy; const canEdit = canEditCard || isCreator; diff --git a/apps/web/src/views/public/board/CardModal.tsx b/apps/web/src/views/public/board/CardModal.tsx index ed555e88..27a95b34 100644 --- a/apps/web/src/views/public/board/CardModal.tsx +++ b/apps/web/src/views/public/board/CardModal.tsx @@ -47,7 +47,7 @@ export function CardModal({ } }; - const { data, isLoading } = api.card.byId.useQuery( + const { data, isLoading, error } = api.card.byId.useQuery( { cardPublicId: cardPublicId ?? "", }, @@ -56,6 +56,17 @@ export function CardModal({ }, ); + // Redirect to 404 if card doesn't exist + useEffect(() => { + if (isOpen && cardPublicId && !isLoading) { + if (error?.data?.code === "NOT_FOUND" || (!data && !isLoading && error)) { + // Close modal first, then redirect + closeModal(); + router.replace("/404"); + } + } + }, [isOpen, cardPublicId, isLoading, error, data, closeModal, router]); + const labels = data?.labels ?? []; const handleScroll = () => { diff --git a/apps/web/src/views/public/boards/index.tsx b/apps/web/src/views/public/boards/index.tsx index 0830bf60..5fcf0eab 100644 --- a/apps/web/src/views/public/boards/index.tsx +++ b/apps/web/src/views/public/boards/index.tsx @@ -1,5 +1,6 @@ import Link from "next/link"; import { useRouter } from "next/router"; +import { useEffect } from "react"; import { t } from "@lingui/core/macro"; import { PageHead } from "~/components/PageHead"; import PatternedBackground from "~/components/PatternedBackground"; @@ -12,13 +13,22 @@ export default function PublicBoardsView() { ? router.query.workspaceSlug[0] : router.query.workspaceSlug; - const { data, isLoading } = api.workspace.bySlug.useQuery( + const { data, isLoading, error } = api.workspace.bySlug.useQuery( { workspaceSlug: workspaceSlug ?? "", }, { enabled: !!workspaceSlug }, ); + // Redirect to 404 if workspace doesn't exist + useEffect(() => { + if (router.isReady && workspaceSlug && !isLoading) { + if (error?.data?.code === "NOT_FOUND" || (!data && !isLoading)) { + router.replace("/404"); + } + } + }, [router, workspaceSlug, isLoading, error, data]); + const BoardsList = ({ isLoading, boards,