diff --git a/gradle/changelog/uniform_configuration_document_titles.yaml b/gradle/changelog/uniform_configuration_document_titles.yaml new file mode 100644 index 0000000000..c4976a352a --- /dev/null +++ b/gradle/changelog/uniform_configuration_document_titles.yaml @@ -0,0 +1,2 @@ +- type: changed + description: Uniform document titles for configuration pages diff --git a/scm-ui/ui-components/src/config/ConfigurationBinder.tsx b/scm-ui/ui-components/src/config/ConfigurationBinder.tsx index a28a09bed4..18dafaa1fb 100644 --- a/scm-ui/ui-components/src/config/ConfigurationBinder.tsx +++ b/scm-ui/ui-components/src/config/ConfigurationBinder.tsx @@ -21,6 +21,8 @@ import { Route } from "react-router-dom"; import { useTranslation, WithTranslation, withTranslation } from "react-i18next"; import { Link, Links, Namespace, Repository } from "@scm-manager/ui-types"; import { urls } from "@scm-manager/ui-api"; +import { useDocumentTitleForRepository } from "@scm-manager/ui-core"; +import { useDocumentTitle } from "@scm-manager/ui-core"; type GlobalRouteProps = { url: string; @@ -75,10 +77,20 @@ class ConfigurationBinder { // route for global configuration, passes the link from the index resource to component const ConfigRoute = ({ url, links, ...additionalProps }: GlobalRouteProps) => { const link = links[linkName]; + + const TitledGlobalSettingComponent: FC = ({ children }) => { + const [t] = useTranslation(this.i18nNamespace); + const [commonTranslation] = useTranslation("commons"); + useDocumentTitle(t(labelI18nKey), commonTranslation("documentTitle.globalConfiguration")); + return <>{children}; + }; + if (link) { return this.route( url + "/settings" + to, - + + + ); } }; @@ -138,7 +150,13 @@ class ConfigurationBinder { binder.bind("repository.route", RepoRoute, repoPredicate); } - bindRepositorySetting(to: string, labelI18nKey: string, linkName: string, RepositoryComponent: any, sortKey?: string) { + bindRepositorySetting( + to: string, + labelI18nKey: string, + linkName: string, + RepositoryComponent: any, + sortKey?: string + ) { // create predicate based on the link name of the current repository route // if the linkname is not available, the navigation link and the route are not bound to the extension points const repoPredicate = (props: any) => { @@ -156,10 +174,24 @@ class ConfigurationBinder { // route for global configuration, passes the current repository to component const RepoRoute = ({ url, repository, ...additionalProps }: RepositoryRouteProps) => { const link = repository._links[linkName]; + + const TitledRepositorySettingComponent: FC = ({ children }) => { + const [t] = useTranslation(this.i18nNamespace); + const [commonTranslation] = useTranslation("commons"); + useDocumentTitleForRepository( + repository, + t(labelI18nKey), + commonTranslation("documentTitle.repositoryConfiguration") + ); + return <>{children}; + }; + if (link) { return this.route( urls.unescapeUrlForRoute(url) + "/settings" + to, - + + + ); } }; @@ -186,10 +218,24 @@ class ConfigurationBinder { const NamespaceRoute: FC = ({ url, namespace, ...additionalProps }) => { const link = namespace._links[linkName]; + + const TitledNamespaceSettingComponent: FC = ({ children }) => { + const [t] = useTranslation(this.i18nNamespace); + const [commonTranslation] = useTranslation("commons"); + useDocumentTitle( + t(labelI18nKey), + commonTranslation("documentTitle.namespaceConfiguration"), + namespace.namespace + ); + return <>{children}; + }; + if (link) { return this.route( urls.unescapeUrlForRoute(url) + "/settings" + to, - + + + ); } return null; diff --git a/scm-ui/ui-core/src/base/helpers/useDocumentTitle.ts b/scm-ui/ui-core/src/base/helpers/useDocumentTitle.ts index f117fd6480..867b04b179 100644 --- a/scm-ui/ui-core/src/base/helpers/useDocumentTitle.ts +++ b/scm-ui/ui-core/src/base/helpers/useDocumentTitle.ts @@ -17,6 +17,7 @@ import { useEffect } from "react"; import { binder, extensionPoints } from "@scm-manager/ui-extensions"; import { Repository } from "@scm-manager/ui-types"; +import { useTranslation } from "react-i18next"; /** * Hook to set the document title. @@ -25,9 +26,10 @@ import { Repository } from "@scm-manager/ui-types"; * Title parts should be sorted with the highest specificity first. */ export default function useDocumentTitle(...titleParts: string[]) { + const [t] = useTranslation("commons"); useEffect(() => { const extension = binder.getExtension("document.title"); - let title = `${titleParts.join(" - ")} - SCM-Manager`; + let title = [...titleParts, t("documentTitle.suffix")].join(" - "); if (extension) { if (typeof extension.documentTitle === "string") { title += ` (${extension.documentTitle})`; diff --git a/scm-ui/ui-webapp/public/locales/de/commons.json b/scm-ui/ui-webapp/public/locales/de/commons.json index 2b58957a83..ce0ebf7c74 100644 --- a/scm-ui/ui-webapp/public/locales/de/commons.json +++ b/scm-ui/ui-webapp/public/locales/de/commons.json @@ -361,5 +361,11 @@ "next": "Fokussiere den nächsten Listeneintrag", "previous": "Fokussiere den vorherigen Listeneintrag" } + }, + "documentTitle": { + "suffix": "SCM-Manager", + "repositoryConfiguration": "Konfiguration", + "namespaceConfiguration": "Konfiguration", + "globalConfiguration": "Konfiguration" } } diff --git a/scm-ui/ui-webapp/public/locales/en/commons.json b/scm-ui/ui-webapp/public/locales/en/commons.json index f06ca9b4bb..bc1787b7af 100644 --- a/scm-ui/ui-webapp/public/locales/en/commons.json +++ b/scm-ui/ui-webapp/public/locales/en/commons.json @@ -362,5 +362,11 @@ "next": "Focus next list item", "previous": "Focus previous list item" } + }, + "documentTitle": { + "suffix": "SCM-Manager", + "repositoryConfiguration": "Configuration", + "namespaceConfiguration": "Configuration", + "globalConfiguration": "Configuration" } } diff --git a/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx b/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx index 9145606716..2c4d764327 100644 --- a/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx +++ b/scm-ui/ui-webapp/src/repos/permissions/containers/Permissions.tsx @@ -45,8 +45,10 @@ const usePermissionData = (namespaceOrRepository: Namespace | Repository) => { const Permissions: FC = ({ namespaceOrRepository }) => { const { isLoading, error, permissions, availablePermissions } = usePermissionData(namespaceOrRepository); const [t] = useTranslation("repos"); + const [commonTranslation] = useTranslation("commons"); useDocumentTitle( t("repositoryRoot.menu.permissionsNavLink"), + commonTranslation(`documentTitle.${isRepository(namespaceOrRepository) ? "repository" : "namespace"}Configuration`), namespaceOrRepository.namespace + (isRepository(namespaceOrRepository) ? "/" + namespaceOrRepository.name : "") );