From 2ebdc2baea17e6ab2d6fc9493e881393f5be37f2 Mon Sep 17 00:00:00 2001 From: Philipinho <16838612+Philipinho@users.noreply.github.com> Date: Mon, 16 Feb 2026 00:33:16 +0000 Subject: [PATCH] empty states --- .../public/locales/en-US/translation.json | 3 ++ .../src/components/common/recent-changes.tsx | 11 ++++--- .../src/components/ui/empty-state.module.css | 8 +++++ apps/client/src/components/ui/empty-state.tsx | 30 +++++++++++++++++++ .../page/tree/components/space-tree.tsx | 12 ++++++-- apps/client/src/pages/page/page.tsx | 26 ++++++++++++++-- 6 files changed, 82 insertions(+), 8 deletions(-) create mode 100644 apps/client/src/components/ui/empty-state.module.css create mode 100644 apps/client/src/components/ui/empty-state.tsx diff --git a/apps/client/public/locales/en-US/translation.json b/apps/client/public/locales/en-US/translation.json index 7011de7c..e46dd2c8 100644 --- a/apps/client/public/locales/en-US/translation.json +++ b/apps/client/public/locales/en-US/translation.json @@ -357,6 +357,9 @@ "Multiple": "Multiple", "Turn into": "Turn into", "Text align": "Text align", + "This page may have been deleted, moved, or you may not have access.": "This page may have been deleted, moved, or you may not have access.", + "Go to homepage": "Go to homepage", + "Pages you create will show up here.": "Pages you create will show up here.", "Heading {{level}}": "Heading {{level}}", "Toggle title": "Toggle title", "Write anything. Enter \"/\" for commands": "Write anything. Enter \"/\" for commands", diff --git a/apps/client/src/components/common/recent-changes.tsx b/apps/client/src/components/common/recent-changes.tsx index 81fdc899..b0bdfec7 100644 --- a/apps/client/src/components/common/recent-changes.tsx +++ b/apps/client/src/components/common/recent-changes.tsx @@ -11,7 +11,8 @@ import PageListSkeleton from "@/components/ui/page-list-skeleton.tsx"; import { buildPageUrl } from "@/features/page/page.utils.ts"; import { formattedDate } from "@/lib/time.ts"; import { useRecentChangesQuery } from "@/features/page/queries/page-query.ts"; -import { IconFileDescription } from "@tabler/icons-react"; +import { IconFileDescription, IconFiles } from "@tabler/icons-react"; +import { EmptyState } from "@/components/ui/empty-state.tsx"; import { getSpaceUrl } from "@/lib/config.ts"; import { useTranslation } from "react-i18next"; import { getInitialsColor } from "@/lib/get-initials-color.ts"; @@ -85,8 +86,10 @@ export default function RecentChanges({ spaceId }: Props) { ) : ( - - {t("No pages yet")} - + ); } diff --git a/apps/client/src/components/ui/empty-state.module.css b/apps/client/src/components/ui/empty-state.module.css new file mode 100644 index 00000000..21d0b810 --- /dev/null +++ b/apps/client/src/components/ui/empty-state.module.css @@ -0,0 +1,8 @@ +.root { + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; + padding: 60px 20px; + text-align: center; +} diff --git a/apps/client/src/components/ui/empty-state.tsx b/apps/client/src/components/ui/empty-state.tsx new file mode 100644 index 00000000..dc016beb --- /dev/null +++ b/apps/client/src/components/ui/empty-state.tsx @@ -0,0 +1,30 @@ +import { Stack, Text } from "@mantine/core"; +import { type TablerIcon } from "@tabler/icons-react"; +import { ReactNode } from "react"; +import classes from "./empty-state.module.css"; + +type EmptyStateProps = { + icon: TablerIcon; + title: string; + description?: string; + action?: ReactNode; +}; + +export function EmptyState({ icon: Icon, title, description, action }: EmptyStateProps) { + return ( +
+ + + + {title} + + {description && ( + + {description} + + )} + {action} + +
+ ); +} diff --git a/apps/client/src/features/page/tree/components/space-tree.tsx b/apps/client/src/features/page/tree/components/space-tree.tsx index f6d1208b..0103c93c 100644 --- a/apps/client/src/features/page/tree/components/space-tree.tsx +++ b/apps/client/src/features/page/tree/components/space-tree.tsx @@ -16,7 +16,7 @@ import { import { useEffect, useRef, useState } from "react"; import { Link, useParams } from "react-router-dom"; import classes from "@/features/page/tree/styles/tree.module.css"; -import { ActionIcon, Box, Menu, rem } from "@mantine/core"; +import { ActionIcon, Box, Menu, rem, Text } from "@mantine/core"; import { IconArrowRight, IconChevronDown, @@ -82,6 +82,7 @@ interface SpaceTreeProps { const openTreeNodesAtom = atom({}); export default function SpaceTree({ spaceId, readOnly }: SpaceTreeProps) { + const { t } = useTranslation(); const { pageSlug } = useParams(); const { data, setData, controllers } = useTreeMutation>(spaceId); @@ -231,11 +232,18 @@ export default function SpaceTree({ spaceId, readOnly }: SpaceTreeProps) { }; }, [setTreeApi]); + const filteredData = data.filter((node) => node?.spaceId === spaceId); + return (
+ {isDataLoaded && filteredData.length === 0 && ( + + {t("No pages yet")} + + )} {isRootReady && rootElement.current && ( node?.spaceId === spaceId)} + data={filteredData} disableDrag={readOnly} disableDrop={readOnly} disableEdit={readOnly} diff --git a/apps/client/src/pages/page/page.tsx b/apps/client/src/pages/page/page.tsx index a1dfee6f..b24348db 100644 --- a/apps/client/src/pages/page/page.tsx +++ b/apps/client/src/pages/page/page.tsx @@ -13,6 +13,10 @@ import { } from "@/features/space/permissions/permissions.type.ts"; import { useTranslation } from "react-i18next"; import React from "react"; +import { EmptyState } from "@/components/ui/empty-state.tsx"; +import { IconFileOff } from "@tabler/icons-react"; +import { Button } from "@mantine/core"; +import { Link } from "react-router-dom"; const MemoizedFullEditor = React.memo(FullEditor); const MemoizedPageHeader = React.memo(PageHeader); @@ -39,9 +43,27 @@ export default function Page() { if (isError || !page) { if ([401, 403, 404].includes(error?.["status"])) { - return
{t("Page not found")}
; + return ( + + {t("Go to homepage")} + + } + /> + ); } - return
{t("Error fetching page data.")}
; + return ( + + ); } if (!space) {