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) {