From 305fd40686fc73a032ef2859539114ce99af5e39 Mon Sep 17 00:00:00 2001 From: Philip Okugbe <16838612+Philipinho@users.noreply.github.com> Date: Mon, 10 Aug 2026 17:38:03 +0100 Subject: [PATCH] fix: browser tab showing url instead of page title (#2382) --- .../src/components/ui/document-title.test.tsx | 45 +++++++++++++++++++ .../src/components/ui/document-title.tsx | 29 ++++++++++++ apps/client/src/components/ui/error-404.tsx | 6 +-- apps/client/src/ee/ai/pages/ai-settings.tsx | 7 +-- .../src/ee/api-key/pages/user-api-keys.tsx | 10 ++--- .../ee/api-key/pages/workspace-api-keys.tsx | 9 +--- apps/client/src/ee/audit/pages/audit-logs.tsx | 9 +--- apps/client/src/ee/billing/pages/billing.tsx | 7 +-- apps/client/src/ee/licence/pages/license.tsx | 7 +-- .../pages/verified-pages.tsx | 9 +--- apps/client/src/ee/pages/cloud-login.tsx | 9 +--- apps/client/src/ee/pages/create-workspace.tsx | 7 +-- .../client/src/ee/security/pages/security.tsx | 8 ++-- .../src/ee/template/pages/template-editor.tsx | 9 +--- .../src/ee/template/pages/template-list.tsx | 9 +--- .../client/src/pages/auth/forgot-password.tsx | 7 +-- apps/client/src/pages/auth/invite-signup.tsx | 7 +-- apps/client/src/pages/auth/login.tsx | 9 +--- apps/client/src/pages/auth/password-reset.tsx | 15 ++----- .../client/src/pages/auth/setup-workspace.tsx | 9 +--- apps/client/src/pages/dashboard/home.tsx | 9 +--- apps/client/src/pages/label/label-page.tsx | 9 +--- apps/client/src/pages/page/page.tsx | 16 ++++--- .../settings/account/account-preferences.tsx | 9 +--- .../settings/account/account-settings.tsx | 9 +--- .../src/pages/settings/group/group-info.tsx | 9 +--- .../src/pages/settings/group/groups.tsx | 7 +-- .../src/pages/settings/shares/shares.tsx | 9 +--- .../src/pages/settings/space/spaces.tsx | 9 +--- .../settings/workspace/workspace-members.tsx | 9 +--- .../settings/workspace/workspace-settings.tsx | 8 ++-- apps/client/src/pages/share/shared-page.tsx | 10 +++-- apps/client/src/pages/space/space-home.tsx | 7 +-- apps/client/src/pages/spaces/spaces.tsx | 9 +--- 34 files changed, 153 insertions(+), 203 deletions(-) create mode 100644 apps/client/src/components/ui/document-title.test.tsx create mode 100644 apps/client/src/components/ui/document-title.tsx diff --git a/apps/client/src/components/ui/document-title.test.tsx b/apps/client/src/components/ui/document-title.test.tsx new file mode 100644 index 000000000..1376bc3a6 --- /dev/null +++ b/apps/client/src/components/ui/document-title.test.tsx @@ -0,0 +1,45 @@ +import { describe, expect, it, beforeEach } from "vitest"; +import { render } from "@testing-library/react"; +import { HelmetProvider } from "react-helmet-async"; +import { DocumentTitle } from "./document-title.tsx"; + +const renderTitle = (ui: React.ReactNode) => + render({ui}); + +describe("DocumentTitle", () => { + beforeEach(() => { + document.head.innerHTML = "Docmost"; + }); + + it("appends the app name", () => { + renderTitle(); + expect(document.title).toBe("Home - Docmost"); + }); + + it("omits the app name when asked", () => { + renderTitle(); + expect(document.title).toBe("My page"); + }); + + it("falls back to the app name without a title", () => { + renderTitle(); + expect(document.title).toBe("Docmost"); + }); + + it("never renders an empty title", () => { + renderTitle(); + const titles = Array.from(document.querySelectorAll("head > title")); + expect(titles.every((node) => node.textContent !== "")).toBe(true); + }); + + it("renders extra head children", () => { + renderTitle( + + + , + ); + expect( + document.querySelector('head > meta[name="robots"]')?.getAttribute("content"), + ).toBe("noindex"); + }); +}); diff --git a/apps/client/src/components/ui/document-title.tsx b/apps/client/src/components/ui/document-title.tsx new file mode 100644 index 000000000..fe878fd5a --- /dev/null +++ b/apps/client/src/components/ui/document-title.tsx @@ -0,0 +1,29 @@ +import React from "react"; +import { Helmet } from "react-helmet-async"; +import { getAppName } from "@/lib/config.ts"; + +type DocumentTitleProps = { + title?: string; + withAppName?: boolean; + children?: React.ReactNode; +}; + +export function DocumentTitle({ + title, + withAppName = true, + children, +}: DocumentTitleProps) { + const appName = getAppName(); + + let documentTitle = appName; + if (title) { + documentTitle = withAppName ? `${title} - ${appName}` : title; + } + + return ( + + {documentTitle} + {children} + + ); +} diff --git a/apps/client/src/components/ui/error-404.tsx b/apps/client/src/components/ui/error-404.tsx index 8c8846aeb..3065b7ecc 100644 --- a/apps/client/src/components/ui/error-404.tsx +++ b/apps/client/src/components/ui/error-404.tsx @@ -1,17 +1,15 @@ import { Title, Text, Button, Container, Group } from "@mantine/core"; import classes from "./error-404.module.css"; import { Link } from "react-router-dom"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export function Error404() { const { t } = useTranslation(); return ( <> - - {t("404 page not found")} - Docmost - + {t("404 page not found")} diff --git a/apps/client/src/ee/ai/pages/ai-settings.tsx b/apps/client/src/ee/ai/pages/ai-settings.tsx index 3a5a281ef..ad678bbcb 100644 --- a/apps/client/src/ee/ai/pages/ai-settings.tsx +++ b/apps/client/src/ee/ai/pages/ai-settings.tsx @@ -1,5 +1,3 @@ -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config.ts"; import SettingsTitle from "@/components/settings/settings-title.tsx"; import React from "react"; import useUserRole from "@/hooks/use-user-role.tsx"; @@ -15,6 +13,7 @@ import { Feature } from "@/ee/features"; import { useUpgradeLabel } from "@/ee/hooks/use-upgrade-label"; import { isCloud } from "@/lib/config.ts"; import { useLocation, useNavigate } from "react-router-dom"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function AiSettings() { const { t } = useTranslation(); @@ -40,9 +39,7 @@ export default function AiSettings() { return ( <> - - AI settings - {getAppName()} - + diff --git a/apps/client/src/ee/api-key/pages/user-api-keys.tsx b/apps/client/src/ee/api-key/pages/user-api-keys.tsx index c305f4afb..2b2572200 100644 --- a/apps/client/src/ee/api-key/pages/user-api-keys.tsx +++ b/apps/client/src/ee/api-key/pages/user-api-keys.tsx @@ -1,10 +1,9 @@ import React, { useState } from "react"; import { Anchor, Alert, Button, Group, Space, Text } from "@mantine/core"; import { IconInfoCircle } from "@tabler/icons-react"; -import { Helmet } from "react-helmet-async"; import { Trans, useTranslation } from "react-i18next"; import SettingsTitle from "@/components/settings/settings-title"; -import { getAppName, getAppUrl } from "@/lib/config"; +import { getAppUrl } from "@/lib/config"; import { ApiKeyTable } from "@/ee/api-key/components/api-key-table"; import { CreateApiKeyModal } from "@/ee/api-key/components/create-api-key-modal"; import { ApiKeyCreatedModal } from "@/ee/api-key/components/api-key-created-modal"; @@ -17,6 +16,7 @@ import { IApiKey } from "@/ee/api-key"; import { useAtom } from "jotai"; import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; import useUserRole from "@/hooks/use-user-role.tsx"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function UserApiKeys() { const { t } = useTranslation(); @@ -49,11 +49,7 @@ export default function UserApiKeys() { return ( <> - - - {t("API keys")} - {getAppName()} - - + diff --git a/apps/client/src/ee/api-key/pages/workspace-api-keys.tsx b/apps/client/src/ee/api-key/pages/workspace-api-keys.tsx index 8476f4457..6a819bbcb 100644 --- a/apps/client/src/ee/api-key/pages/workspace-api-keys.tsx +++ b/apps/client/src/ee/api-key/pages/workspace-api-keys.tsx @@ -1,9 +1,7 @@ import React, { useState } from "react"; import { Anchor, Button, Divider, Group, Space, Text } from "@mantine/core"; -import { Helmet } from "react-helmet-async"; import { Trans, useTranslation } from "react-i18next"; import SettingsTitle from "@/components/settings/settings-title"; -import { getAppName } from "@/lib/config"; import { ApiKeyTable } from "@/ee/api-key/components/api-key-table"; import { CreateApiKeyModal } from "@/ee/api-key/components/create-api-key-modal"; import { ApiKeyCreatedModal } from "@/ee/api-key/components/api-key-created-modal"; @@ -15,6 +13,7 @@ import { useGetApiKeysQuery } from "@/ee/api-key/queries/api-key-query.ts"; import { IApiKey } from "@/ee/api-key"; import useUserRole from '@/hooks/use-user-role.tsx'; import RestrictApiToAdmins from "@/ee/api-key/components/restrict-api-to-admins"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function WorkspaceApiKeys() { const { t } = useTranslation(); @@ -47,11 +46,7 @@ export default function WorkspaceApiKeys() { return ( <> - - - {t("API management")} - {getAppName()} - - + diff --git a/apps/client/src/ee/audit/pages/audit-logs.tsx b/apps/client/src/ee/audit/pages/audit-logs.tsx index 05f7881ad..811e094e8 100644 --- a/apps/client/src/ee/audit/pages/audit-logs.tsx +++ b/apps/client/src/ee/audit/pages/audit-logs.tsx @@ -10,11 +10,9 @@ import { Text, Tooltip, } from "@mantine/core"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; import { IconSettings } from "@tabler/icons-react"; import SettingsTitle from "@/components/settings/settings-title"; -import { getAppName } from "@/lib/config"; import Paginate from "@/components/common/paginate"; import { useCursorPaginate } from "@/hooks/use-cursor-paginate"; import { @@ -26,6 +24,7 @@ import { IAuditLogParams } from "@/ee/audit/types/audit.types"; import { eventFilterOptions } from "@/ee/audit/lib/audit-event-labels"; import AuditLogsTable from "@/ee/audit/components/audit-logs-table"; import useUserRole from "@/hooks/use-user-role"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; type RetentionUnit = "days" | "months" | "years"; @@ -97,11 +96,7 @@ export default function AuditLogs() { return ( <> - - - {t("Audit log")} - {getAppName()} - - + diff --git a/apps/client/src/ee/billing/pages/billing.tsx b/apps/client/src/ee/billing/pages/billing.tsx index a389a1e58..9f47b9a58 100644 --- a/apps/client/src/ee/billing/pages/billing.tsx +++ b/apps/client/src/ee/billing/pages/billing.tsx @@ -1,5 +1,3 @@ -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config.ts"; import SettingsTitle from "@/components/settings/settings-title.tsx"; import BillingPlans from "@/ee/billing/components/billing-plans.tsx"; import BillingTrial from "@/ee/billing/components/billing-trial.tsx"; @@ -9,6 +7,7 @@ import React from "react"; import BillingDetails from "@/ee/billing/components/billing-details.tsx"; import { useBillingQuery } from "@/ee/billing/queries/billing-query.ts"; import useUserRole from "@/hooks/use-user-role.tsx"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function Billing() { const { data: billing, isError: isBillingError } = useBillingQuery(); @@ -20,9 +19,7 @@ export default function Billing() { return ( <> - - Billing - {getAppName()} - + diff --git a/apps/client/src/ee/licence/pages/license.tsx b/apps/client/src/ee/licence/pages/license.tsx index 0aa9d2f58..1e9727d23 100644 --- a/apps/client/src/ee/licence/pages/license.tsx +++ b/apps/client/src/ee/licence/pages/license.tsx @@ -1,5 +1,3 @@ -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config.ts"; import SettingsTitle from "@/components/settings/settings-title.tsx"; import React from "react"; import useUserRole from "@/hooks/use-user-role.tsx"; @@ -9,6 +7,7 @@ import InstallationDetails from "@/ee/licence/components/installation-details.ts import OssDetails from "@/ee/licence/components/oss-details.tsx"; import { useAtom } from "jotai/index"; import { entitlementAtom } from "@/ee/entitlement/entitlement-atom"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function License() { const [entitlements] = useAtom(entitlementAtom); @@ -21,9 +20,7 @@ export default function License() { return ( <> - - License - {getAppName()} - + diff --git a/apps/client/src/ee/page-verification/pages/verified-pages.tsx b/apps/client/src/ee/page-verification/pages/verified-pages.tsx index 51786e5a1..15b7badad 100644 --- a/apps/client/src/ee/page-verification/pages/verified-pages.tsx +++ b/apps/client/src/ee/page-verification/pages/verified-pages.tsx @@ -1,17 +1,16 @@ import { useState, useMemo } from "react"; import { Group, MultiSelect, Select, Space, TextInput } from "@mantine/core"; import { useDebouncedValue } from "@mantine/hooks"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; import { IconSearch } from "@tabler/icons-react"; import SettingsTitle from "@/components/settings/settings-title"; -import { getAppName } from "@/lib/config"; import Paginate from "@/components/common/paginate"; import { useCursorPaginate } from "@/hooks/use-cursor-paginate"; import { useVerificationListQuery } from "@/ee/page-verification/queries/page-verification-query"; import { IVerificationListParams } from "@/ee/page-verification/types/page-verification.types"; import VerificationListTable from "@/ee/page-verification/components/verification-list-table"; import { useGetSpacesQuery } from "@/features/space/queries/space-query"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function VerifiedPages() { const { t } = useTranslation(); @@ -68,11 +67,7 @@ export default function VerifiedPages() { return ( <> - - - {t("Verified pages")} - {getAppName()} - - + diff --git a/apps/client/src/ee/pages/cloud-login.tsx b/apps/client/src/ee/pages/cloud-login.tsx index c0a40a0d4..14de528b8 100644 --- a/apps/client/src/ee/pages/cloud-login.tsx +++ b/apps/client/src/ee/pages/cloud-login.tsx @@ -1,18 +1,13 @@ -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config.ts"; import { CloudLoginForm } from "@/ee/components/cloud-login-form.tsx"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function CloudLogin() { const { t } = useTranslation(); return ( <> - - - {t("Login")} - {getAppName()} - - + diff --git a/apps/client/src/ee/pages/create-workspace.tsx b/apps/client/src/ee/pages/create-workspace.tsx index fb335d662..f681e65e9 100644 --- a/apps/client/src/ee/pages/create-workspace.tsx +++ b/apps/client/src/ee/pages/create-workspace.tsx @@ -1,14 +1,11 @@ import { SetupWorkspaceForm } from "@/features/auth/components/setup-workspace-form.tsx"; -import { Helmet } from "react-helmet-async"; import React from "react"; -import { getAppName } from "@/lib/config.ts"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function CreateWorkspace() { return ( <> - - Create Workspace - {getAppName()} - + ); diff --git a/apps/client/src/ee/security/pages/security.tsx b/apps/client/src/ee/security/pages/security.tsx index 2ff3670be..de3a7073b 100644 --- a/apps/client/src/ee/security/pages/security.tsx +++ b/apps/client/src/ee/security/pages/security.tsx @@ -1,5 +1,4 @@ -import { Helmet } from "react-helmet-async"; -import { getAppName, isCloud } from "@/lib/config.ts"; +import { isCloud } from "@/lib/config.ts"; import SettingsTitle from "@/components/settings/settings-title.tsx"; import { Alert, @@ -37,6 +36,7 @@ import EnableScim from "@/ee/scim/components/enable-scim"; import { useCursorPaginate } from "@/hooks/use-cursor-paginate"; import Paginate from "@/components/common/paginate"; import { IScimToken } from "@/ee/scim/types/scim-token.types"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; const SCIM_TOKEN_LIMIT = 5; @@ -64,9 +64,7 @@ export default function Security() { return ( <> - - Security - {getAppName()} - + diff --git a/apps/client/src/ee/template/pages/template-editor.tsx b/apps/client/src/ee/template/pages/template-editor.tsx index 2b4069978..7fadf05c8 100644 --- a/apps/client/src/ee/template/pages/template-editor.tsx +++ b/apps/client/src/ee/template/pages/template-editor.tsx @@ -22,8 +22,6 @@ import { useTranslation } from "react-i18next"; import { useDisclosure, useWindowEvent } from "@mantine/hooks"; import { notifications } from "@mantine/notifications"; import { Link, useParams } from "react-router-dom"; -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config"; import { useEditor, EditorContent } from "@tiptap/react"; import { templateExtensions } from "@/features/editor/extensions/extensions"; import { @@ -44,6 +42,7 @@ import CalloutMenu from "@/features/editor/components/callout/callout-menu.tsx"; import ColumnsMenu from "@/features/editor/components/columns/columns-menu.tsx"; import classes from "./template-editor.module.css"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function TemplateEditor() { const { t } = useTranslation(); @@ -247,11 +246,7 @@ export default function TemplateEditor() { return ( <> - - - {t("Edit template")} - {getAppName()} - - + {editorToolbarEnabled && editor && ( diff --git a/apps/client/src/ee/template/pages/template-list.tsx b/apps/client/src/ee/template/pages/template-list.tsx index fafd4c117..7fc740304 100644 --- a/apps/client/src/ee/template/pages/template-list.tsx +++ b/apps/client/src/ee/template/pages/template-list.tsx @@ -13,11 +13,9 @@ import { } from "@mantine/core"; import { modals } from "@mantine/modals"; import { IconPlus } from "@tabler/icons-react"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; import { useNavigate } from "react-router-dom"; import { useDisclosure } from "@mantine/hooks"; -import { getAppName } from "@/lib/config"; import { useGetTemplatesQuery, useDeleteTemplateMutation, @@ -31,6 +29,7 @@ import useUserRole from "@/hooks/use-user-role"; import CreateTemplateModal from "@/ee/template/components/create-template-modal"; import { useAtomValue } from "jotai"; import { workspaceAtom } from "@/features/user/atoms/current-user-atom"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function TemplateList() { const { t } = useTranslation(); @@ -102,11 +101,7 @@ export default function TemplateList() { return ( <> - - - {t("Templates")} - {getAppName()} - - + diff --git a/apps/client/src/pages/auth/forgot-password.tsx b/apps/client/src/pages/auth/forgot-password.tsx index 94826c1c8..dc5f10826 100644 --- a/apps/client/src/pages/auth/forgot-password.tsx +++ b/apps/client/src/pages/auth/forgot-password.tsx @@ -1,13 +1,10 @@ import { ForgotPasswordForm } from "@/features/auth/components/forgot-password-form"; -import { getAppName } from "@/lib/config"; -import { Helmet } from "react-helmet-async"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function ForgotPassword() { return ( <> - - Forgot Password - {getAppName()} - + ); diff --git a/apps/client/src/pages/auth/invite-signup.tsx b/apps/client/src/pages/auth/invite-signup.tsx index b9c9340ce..0ed064bf6 100644 --- a/apps/client/src/pages/auth/invite-signup.tsx +++ b/apps/client/src/pages/auth/invite-signup.tsx @@ -1,16 +1,13 @@ -import { Helmet } from "react-helmet-async"; import { InviteSignUpForm } from "@/features/auth/components/invite-sign-up-form.tsx"; -import {getAppName} from "@/lib/config.ts"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function InviteSignup() { const { t } = useTranslation(); return ( <> - - {t("Invitation Signup")} - {getAppName()} - + ); diff --git a/apps/client/src/pages/auth/login.tsx b/apps/client/src/pages/auth/login.tsx index 4b062c190..5cd710baf 100644 --- a/apps/client/src/pages/auth/login.tsx +++ b/apps/client/src/pages/auth/login.tsx @@ -1,18 +1,13 @@ import { LoginForm } from "@/features/auth/components/login-form"; -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config.ts"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function LoginPage() { const { t } = useTranslation(); return ( <> - - - {t("Login")} - {getAppName()} - - + ); diff --git a/apps/client/src/pages/auth/password-reset.tsx b/apps/client/src/pages/auth/password-reset.tsx index ae7d391b3..d785761ee 100644 --- a/apps/client/src/pages/auth/password-reset.tsx +++ b/apps/client/src/pages/auth/password-reset.tsx @@ -1,11 +1,10 @@ -import { Helmet } from "react-helmet-async"; import { PasswordResetForm } from "@/features/auth/components/password-reset-form"; import { Link, useSearchParams } from "react-router-dom"; import { useVerifyUserTokenQuery } from "@/features/auth/queries/auth-query"; import { Button, Container, Group, Text } from "@mantine/core"; import APP_ROUTE from "@/lib/app-route"; -import { getAppName } from "@/lib/config.ts"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function PasswordReset() { const { t } = useTranslation(); @@ -23,11 +22,7 @@ export default function PasswordReset() { if (isError || !resetToken) { return ( <> - - - {t("Password Reset")} - {getAppName()} - - + {t("Invalid or expired password reset link")} @@ -49,11 +44,7 @@ export default function PasswordReset() { return ( <> - - - {t("Password Reset")} - {getAppName()} - - + ); diff --git a/apps/client/src/pages/auth/setup-workspace.tsx b/apps/client/src/pages/auth/setup-workspace.tsx index 7fb75e2db..0e85525a9 100644 --- a/apps/client/src/pages/auth/setup-workspace.tsx +++ b/apps/client/src/pages/auth/setup-workspace.tsx @@ -1,11 +1,10 @@ import { useWorkspacePublicDataQuery } from "@/features/workspace/queries/workspace-query.ts"; import { SetupWorkspaceForm } from "@/features/auth/components/setup-workspace-form.tsx"; -import { Helmet } from "react-helmet-async"; import React, { useEffect } from "react"; import { useNavigate } from "react-router-dom"; import APP_ROUTE from "@/lib/app-route.ts"; -import { getAppName } from "@/lib/config.ts"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function SetupWorkspace() { const { t } = useTranslation(); @@ -35,11 +34,7 @@ export default function SetupWorkspace() { ) { return ( <> - - - {t("Setup Workspace")} - {getAppName()} - - + ); diff --git a/apps/client/src/pages/dashboard/home.tsx b/apps/client/src/pages/dashboard/home.tsx index 83d553038..20d127bef 100644 --- a/apps/client/src/pages/dashboard/home.tsx +++ b/apps/client/src/pages/dashboard/home.tsx @@ -2,20 +2,15 @@ import { Container, Space } from "@mantine/core"; import HomeTabs from "@/features/home/components/home-tabs"; import HomeAiPrompt from "@/features/home/components/home-ai-prompt"; import SpaceCarousel from "@/features/space/components/space-carousel.tsx"; -import { getAppName } from "@/lib/config.ts"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function Home() { const { t } = useTranslation(); return ( <> - - - {t("Home")} - {getAppName()} - - + diff --git a/apps/client/src/pages/label/label-page.tsx b/apps/client/src/pages/label/label-page.tsx index 8a5a53532..c272d7778 100644 --- a/apps/client/src/pages/label/label-page.tsx +++ b/apps/client/src/pages/label/label-page.tsx @@ -17,9 +17,7 @@ import { } from "@tabler/icons-react"; import { Link, useParams } from "react-router-dom"; import { useTranslation } from "react-i18next"; -import { Helmet } from "react-helmet-async"; import { useDebouncedValue } from "@mantine/hooks"; -import { getAppName } from "@/lib/config"; import { useLabelPagesQuery } from "@/features/label/queries/label-query.ts"; import { useGetSpacesQuery } from "@/features/space/queries/space-query.ts"; import { getLabelColor } from "@/features/label/utils/label-colors.ts"; @@ -29,6 +27,7 @@ import { normalizeLabelName } from "@/features/label/utils/normalize-label.ts"; import { SpaceFilterMenu } from "@/features/space/components/space-filter-menu.tsx"; import { EmptyState } from "@/components/ui/empty-state"; import classes from "@/features/label/label.module.css"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function LabelPage() { const { t } = useTranslation(); @@ -82,11 +81,7 @@ export default function LabelPage() { return ( <> - - - {labelName} - {getAppName()} - - + diff --git a/apps/client/src/pages/page/page.tsx b/apps/client/src/pages/page/page.tsx index 5ca737d02..ea109488f 100644 --- a/apps/client/src/pages/page/page.tsx +++ b/apps/client/src/pages/page/page.tsx @@ -3,7 +3,6 @@ import { usePageQuery } from "@/features/page/queries/page-query"; import { FullEditor } from "@/features/editor/full-editor"; import { TitleEditor } from "@/features/editor/title-editor"; import HistoryModal from "@/features/page-history/components/history-modal"; -import { Helmet } from "react-helmet-async"; import PageHeader from "@/features/page/components/header/page-header.tsx"; import { extractPageSlugId } from "@/lib"; import { useGetSpaceBySlugQuery } from "@/features/space/queries/space-query.ts"; @@ -18,6 +17,7 @@ import { BaseView } from "@/ee/base/components/base-view"; import { useHasFeature } from "@/ee/hooks/use-feature"; import { Feature } from "@/ee/features"; import { getPageTitle } from "@/features/page/page.utils"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; const MemoizedFullEditor = React.memo(FullEditor); const MemoizedTitleEditor = React.memo(TitleEditor); const MemoizedPageHeader = React.memo(PageHeader); @@ -110,9 +110,10 @@ function PageContent({ pageSlug }: { pageSlug: string | undefined }) { paddingTop: "calc(var(--page-header-height) + 6px)", }} > - - {`${page?.icon || ""} ${getPageTitle(page?.title, page?.isBase, t)}`} - +
- - {`${page?.icon || ""} ${getPageTitle(page?.title, page?.isBase, t)}`} - + diff --git a/apps/client/src/pages/settings/account/account-preferences.tsx b/apps/client/src/pages/settings/account/account-preferences.tsx index bead4bfbc..6f9a0c1b4 100644 --- a/apps/client/src/pages/settings/account/account-preferences.tsx +++ b/apps/client/src/pages/settings/account/account-preferences.tsx @@ -5,21 +5,16 @@ import PageWidthPref from "@/features/user/components/page-width-pref.tsx"; import PageEditPref from "@/features/user/components/page-state-pref"; import FixedToolbarPref from "@/features/user/components/fixed-toolbar-pref"; import NotificationPref from "@/features/user/components/notification-pref"; -import { getAppName } from "@/lib/config.ts"; import { Divider } from "@mantine/core"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function AccountPreferences() { const { t } = useTranslation(); return ( <> - - - {t("Preferences")} - {getAppName()} - - + diff --git a/apps/client/src/pages/settings/account/account-settings.tsx b/apps/client/src/pages/settings/account/account-settings.tsx index 6f87d31af..f39a0e877 100644 --- a/apps/client/src/pages/settings/account/account-settings.tsx +++ b/apps/client/src/pages/settings/account/account-settings.tsx @@ -4,22 +4,17 @@ import ChangePassword from "@/features/user/components/change-password"; import { Divider } from "@mantine/core"; import AccountAvatar from "@/features/user/components/account-avatar"; import SettingsTitle from "@/components/settings/settings-title.tsx"; -import { getAppName } from "@/lib/config.ts"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; import { AccountMfaSection } from "@/features/user/components/account-mfa-section"; import SessionList from "@/features/session/components/session-list"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function AccountSettings() { const { t } = useTranslation(); return ( <> - - - {t("My Profile")} - {getAppName()} - - + diff --git a/apps/client/src/pages/settings/group/group-info.tsx b/apps/client/src/pages/settings/group/group-info.tsx index 5a1c9bb71..70c596c5b 100644 --- a/apps/client/src/pages/settings/group/group-info.tsx +++ b/apps/client/src/pages/settings/group/group-info.tsx @@ -1,20 +1,15 @@ import SettingsTitle from "@/components/settings/settings-title.tsx"; import GroupMembersList from "@/features/group/components/group-members"; import GroupDetails from "@/features/group/components/group-details"; -import { getAppName } from "@/lib/config.ts"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function GroupInfo() { const { t } = useTranslation(); return ( <> - - - {t("Manage Group")} - {getAppName()} - - + diff --git a/apps/client/src/pages/settings/group/groups.tsx b/apps/client/src/pages/settings/group/groups.tsx index cae553f15..0b75b9bfd 100644 --- a/apps/client/src/pages/settings/group/groups.tsx +++ b/apps/client/src/pages/settings/group/groups.tsx @@ -3,9 +3,8 @@ import SettingsTitle from "@/components/settings/settings-title.tsx"; import { Group } from "@mantine/core"; import CreateGroupModal from "@/features/group/components/create-group-modal"; import useUserRole from "@/hooks/use-user-role.tsx"; -import {getAppName} from "@/lib/config.ts"; -import {Helmet} from "react-helmet-async"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function Groups() { const { t } = useTranslation(); @@ -13,9 +12,7 @@ export default function Groups() { return ( <> - - {t("Groups")} - {getAppName()} - + diff --git a/apps/client/src/pages/settings/shares/shares.tsx b/apps/client/src/pages/settings/shares/shares.tsx index 1a5a118e4..d327681af 100644 --- a/apps/client/src/pages/settings/shares/shares.tsx +++ b/apps/client/src/pages/settings/shares/shares.tsx @@ -1,22 +1,17 @@ import SettingsTitle from "@/components/settings/settings-title.tsx"; -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config.ts"; import { useTranslation } from "react-i18next"; import ShareList from "@/features/share/components/share-list.tsx"; import { Alert, Text } from "@mantine/core"; import { IconInfoCircle } from "@tabler/icons-react"; import React from "react"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function Shares() { const { t } = useTranslation(); return ( <> - - - {t("Public sharing")} - {getAppName()} - - + }> diff --git a/apps/client/src/pages/settings/space/spaces.tsx b/apps/client/src/pages/settings/space/spaces.tsx index 329eab25f..e098f3570 100644 --- a/apps/client/src/pages/settings/space/spaces.tsx +++ b/apps/client/src/pages/settings/space/spaces.tsx @@ -3,9 +3,8 @@ import SpaceList from "@/features/space/components/space-list.tsx"; import useUserRole from "@/hooks/use-user-role.tsx"; import { Group } from "@mantine/core"; import CreateSpaceModal from "@/features/space/components/create-space-modal.tsx"; -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config.ts"; import { useTranslation } from "react-i18next"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function Spaces() { const { t } = useTranslation(); @@ -13,11 +12,7 @@ export default function Spaces() { return ( <> - - - {t("Spaces")} - {getAppName()} - - + diff --git a/apps/client/src/pages/settings/workspace/workspace-members.tsx b/apps/client/src/pages/settings/workspace/workspace-members.tsx index dba853a47..d12bed760 100644 --- a/apps/client/src/pages/settings/workspace/workspace-members.tsx +++ b/apps/client/src/pages/settings/workspace/workspace-members.tsx @@ -6,11 +6,10 @@ import { useEffect, useState } from "react"; import { useNavigate, useSearchParams } from "react-router-dom"; import WorkspaceInvitesTable from "@/features/workspace/components/members/components/workspace-invites-table.tsx"; import useUserRole from "@/hooks/use-user-role.tsx"; -import { getAppName } from "@/lib/config.ts"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; import { useAtom } from "jotai"; import { workspaceAtom } from "@/features/user/atoms/current-user-atom.ts"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function WorkspaceMembers() { const { t } = useTranslation(); @@ -38,11 +37,7 @@ export default function WorkspaceMembers() { return ( <> - - - {t("Members")} - {getAppName()} - - + {/* */} diff --git a/apps/client/src/pages/settings/workspace/workspace-settings.tsx b/apps/client/src/pages/settings/workspace/workspace-settings.tsx index 1dc55485f..dc9e7c5fc 100644 --- a/apps/client/src/pages/settings/workspace/workspace-settings.tsx +++ b/apps/client/src/pages/settings/workspace/workspace-settings.tsx @@ -2,21 +2,19 @@ import SettingsTitle from "@/components/settings/settings-title.tsx"; import WorkspaceNameForm from "@/features/workspace/components/settings/components/workspace-name-form"; import WorkspaceIcon from "@/features/workspace/components/settings/components/workspace-icon.tsx"; import { useTranslation } from "react-i18next"; -import { getAppName, isCloud } from "@/lib/config.ts"; -import { Helmet } from "react-helmet-async"; +import { isCloud } from "@/lib/config.ts"; import ManageHostname from "@/ee/components/manage-hostname.tsx"; import { Divider } from "@mantine/core"; import AllowMemberTemplates from "@/ee/security/components/allow-member-templates.tsx"; import WorkspaceDefaultPageEditMode from "@/features/workspace/components/settings/components/workspace-default-page-edit-mode.tsx"; import PersonalSpacesSetting from "@/ee/personal-space/components/personal-spaces-setting.tsx"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function WorkspaceSettings() { const { t } = useTranslation(); return ( <> - - Workspace Settings - {getAppName()} - + diff --git a/apps/client/src/pages/share/shared-page.tsx b/apps/client/src/pages/share/shared-page.tsx index f156208e5..b7028b2fb 100644 --- a/apps/client/src/pages/share/shared-page.tsx +++ b/apps/client/src/pages/share/shared-page.tsx @@ -1,5 +1,4 @@ import { useNavigate, useParams } from "react-router-dom"; -import { Helmet } from "react-helmet-async"; import { useTranslation } from "react-i18next"; import { useSharePageQuery } from "@/features/share/queries/share-query.ts"; import { Container } from "@mantine/core"; @@ -14,6 +13,7 @@ import { sharedTreeDataAtom, } from "@/features/share/atoms/shared-page-atom.ts"; import { isPageInTree } from "@/features/share/utils.ts"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function SharedPage() { const { t } = useTranslation(); @@ -56,12 +56,14 @@ export default function SharedPage() { return (
- - {`${data?.page?.title || t("untitled")}`} + {!data?.share.searchIndexing && ( )} - + - - {space?.name || 'Overview'} - {getAppName()} - + {space && } diff --git a/apps/client/src/pages/spaces/spaces.tsx b/apps/client/src/pages/spaces/spaces.tsx index 1eadcee79..0f67c8c63 100644 --- a/apps/client/src/pages/spaces/spaces.tsx +++ b/apps/client/src/pages/spaces/spaces.tsx @@ -1,13 +1,12 @@ import { Container, Title, Text, Group, Box } from "@mantine/core"; import { useTranslation } from "react-i18next"; -import { Helmet } from "react-helmet-async"; -import { getAppName } from "@/lib/config"; import { useGetSpacesQuery } from "@/features/space/queries/space-query"; import CreateSpaceModal from "@/features/space/components/create-space-modal"; import { AllSpacesList } from "@/features/space/components/spaces-page"; import FavoriteSpacesGrid from "@/features/space/components/spaces-page/favorite-spaces-grid"; import { usePaginateAndSearch } from "@/hooks/use-paginate-and-search"; import useUserRole from "@/hooks/use-user-role"; +import { DocumentTitle } from "@/components/ui/document-title.tsx"; export default function Spaces() { const { t } = useTranslation(); @@ -22,11 +21,7 @@ export default function Spaces() { return ( <> - - - {t("Spaces")} - {getAppName()} - - +