From bb2895689fb006897bc244271aca6f0bfa1839b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E0=A4=95=E0=A4=BE=E0=A4=B0=E0=A4=A4=E0=A5=8B=E0=A4=AB?= =?UTF-8?q?=E0=A5=8D=E0=A4=AB=E0=A5=87=E0=A4=B2=E0=A4=B8=E0=A5=8D=E0=A4=95?= =?UTF-8?q?=E0=A5=8D=E0=A4=B0=E0=A4=BF=E0=A4=AA=E0=A5=8D=E0=A4=9F=E2=84=A2?= Date: Mon, 30 Sep 2024 10:57:25 +0200 Subject: [PATCH] feat(editor): Overhaul document title management (#10999) --- .../components/MainHeader/WorkflowDetails.vue | 6 ++-- .../src/components/WorkerList.ee.vue | 5 +-- .../__tests__/useDocumentTitle.test.ts | 31 +++++++++++++++++++ .../__tests__/useRunWorkflow.spec.ts | 5 +-- .../src/composables/useDocumentTitle.ts | 21 +++++++++++++ .../src/composables/usePushConnection.ts | 8 ++--- .../src/composables/useRunWorkflow.ts | 10 +++--- .../src/composables/useTitleChange.ts | 30 ------------------ .../src/composables/useWorkflowHelpers.ts | 14 +++++++++ .../editor-ui/src/stores/settings.store.ts | 4 --- packages/editor-ui/src/utils/htmlUtils.ts | 4 --- .../editor-ui/src/views/CredentialsView.vue | 3 ++ .../editor-ui/src/views/ExecutionsView.vue | 5 +-- packages/editor-ui/src/views/NodeView.v2.vue | 12 +++---- packages/editor-ui/src/views/NodeView.vue | 16 +++++----- .../editor-ui/src/views/ProjectSettings.vue | 3 ++ .../editor-ui/src/views/SettingsApiView.vue | 3 ++ .../src/views/SettingsCommunityNodesView.vue | 3 ++ .../src/views/SettingsExternalSecrets.vue | 3 ++ .../editor-ui/src/views/SettingsLdapView.vue | 3 ++ .../src/views/SettingsLogStreamingView.vue | 3 ++ .../src/views/SettingsPersonalView.vue | 3 ++ .../src/views/SettingsSourceControl.vue | 3 ++ packages/editor-ui/src/views/SettingsSso.vue | 3 ++ .../src/views/SettingsUsageAndPlan.vue | 3 ++ .../editor-ui/src/views/SettingsUsersView.vue | 4 +++ .../src/views/TemplatesCollectionView.vue | 7 +++-- .../src/views/TemplatesSearchView.vue | 5 +-- .../src/views/TemplatesWorkflowView.vue | 7 +++-- .../editor-ui/src/views/VariablesView.vue | 8 ++++- .../editor-ui/src/views/WorkflowsView.vue | 3 ++ 31 files changed, 155 insertions(+), 83 deletions(-) create mode 100644 packages/editor-ui/src/composables/__tests__/useDocumentTitle.test.ts create mode 100644 packages/editor-ui/src/composables/useDocumentTitle.ts delete mode 100644 packages/editor-ui/src/composables/useTitleChange.ts diff --git a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue index 0cc6dfe58e08b..4a04c141d2ae4 100644 --- a/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue +++ b/packages/editor-ui/src/components/MainHeader/WorkflowDetails.vue @@ -34,7 +34,7 @@ import { useWorkflowsStore } from '@/stores/workflows.store'; import { useProjectsStore } from '@/stores/projects.store'; import { saveAs } from 'file-saver'; -import { useTitleChange } from '@/composables/useTitleChange'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import { useMessage } from '@/composables/useMessage'; import { useToast } from '@/composables/useToast'; import { getResourcePermissions } from '@/permissions'; @@ -87,7 +87,7 @@ const locale = useI18n(); const telemetry = useTelemetry(); const message = useMessage(); const toast = useToast(); -const titleChange = useTitleChange(); +const documentTitle = useDocumentTitle(); const workflowHelpers = useWorkflowHelpers({ router }); const isTagsEditEnabled = ref(false); @@ -558,7 +558,7 @@ async function onWorkflowMenuSelect(action: WORKFLOW_MENU_ACTIONS): Promise({ releaseChannel: 'stable' }); +vi.mock('@/stores/settings.store', () => ({ + useSettingsStore: vi.fn(() => ({ settings })), +})); + +describe('useDocumentTitle', () => { + it('should set the document title', () => { + const { set } = useDocumentTitle(); + set('Test Title'); + expect(document.title).toBe('Test Title - n8n'); + }); + + it('should reset the document title', () => { + const { set, reset } = useDocumentTitle(); + set('Test Title'); + reset(); + expect(document.title).toBe('Workflow Automation - n8n'); + }); + + it('should use the correct prefix for the release channel', () => { + settings.releaseChannel = 'beta'; + const { set } = useDocumentTitle(); + set('Test Title'); + expect(document.title).toBe('Test Title - n8n[BETA]'); + }); +}); diff --git a/packages/editor-ui/src/composables/__tests__/useRunWorkflow.spec.ts b/packages/editor-ui/src/composables/__tests__/useRunWorkflow.spec.ts index a1583280a9ab8..4b6fde39918f7 100644 --- a/packages/editor-ui/src/composables/__tests__/useRunWorkflow.spec.ts +++ b/packages/editor-ui/src/composables/__tests__/useRunWorkflow.spec.ts @@ -52,6 +52,7 @@ vi.mock('@/composables/useWorkflowHelpers', () => ({ getCurrentWorkflow: vi.fn(), saveCurrentWorkflow: vi.fn(), getWorkflowDataToSave: vi.fn(), + setDocumentTitle: vi.fn(), }), })); @@ -61,10 +62,6 @@ vi.mock('@/composables/useNodeHelpers', () => ({ }), })); -vi.mock('@/composables/useTitleChange', () => ({ - useTitleChange: vi.fn().mockReturnValue({ titleSet: vi.fn() }), -})); - vi.mock('vue-router', async (importOriginal) => { const { RouterLink } = await importOriginal(); return { diff --git a/packages/editor-ui/src/composables/useDocumentTitle.ts b/packages/editor-ui/src/composables/useDocumentTitle.ts new file mode 100644 index 0000000000000..9b0ff97381004 --- /dev/null +++ b/packages/editor-ui/src/composables/useDocumentTitle.ts @@ -0,0 +1,21 @@ +import { useSettingsStore } from '@/stores/settings.store'; + +const DEFAULT_TITLE = 'Workflow Automation'; + +export function useDocumentTitle() { + const settingsStore = useSettingsStore(); + const { releaseChannel } = settingsStore.settings; + const suffix = + !releaseChannel || releaseChannel === 'stable' ? 'n8n' : `n8n[${releaseChannel.toUpperCase()}]`; + + const set = (title: string) => { + const sections = [title || DEFAULT_TITLE, suffix]; + document.title = sections.join(' - '); + }; + + const reset = () => { + set(''); + }; + + return { set, reset }; +} diff --git a/packages/editor-ui/src/composables/usePushConnection.ts b/packages/editor-ui/src/composables/usePushConnection.ts index 347fc6d0bb414..4ced982610658 100644 --- a/packages/editor-ui/src/composables/usePushConnection.ts +++ b/packages/editor-ui/src/composables/usePushConnection.ts @@ -18,7 +18,6 @@ import type { PushMessage, PushPayload } from '@n8n/api-types'; import type { IExecutionResponse, IExecutionsCurrentSummaryExtended } from '@/Interface'; import { useNodeHelpers } from '@/composables/useNodeHelpers'; -import { useTitleChange } from '@/composables/useTitleChange'; import { useToast } from '@/composables/useToast'; import { WORKFLOW_SETTINGS_MODAL_KEY } from '@/constants'; import { getTriggerNodeServiceName } from '@/utils/nodeTypesUtils'; @@ -43,7 +42,6 @@ type IPushDataExecutionFinishedPayload = PushPayload<'executionFinished'>; export function usePushConnection({ router }: { router: ReturnType }) { const workflowHelpers = useWorkflowHelpers({ router }); const nodeHelpers = useNodeHelpers(); - const titleChange = useTitleChange(); const toast = useToast(); const i18n = useI18n(); const telemetry = useTelemetry(); @@ -324,7 +322,7 @@ export function usePushConnection({ router }: { router: ReturnTypeMore info`, @@ -333,7 +331,7 @@ export function usePushConnection({ router }: { router: ReturnType { - const settingsStore = useSettingsStore(); - const { releaseChannel } = settingsStore.settings; - return releaseChannel === 'stable' ? title : `[${releaseChannel.toUpperCase()}] ${title}`; - }; - - const titleSet = (workflow: string, status: WorkflowTitleStatus) => { - let icon = '⚠️'; - if (status === 'EXECUTING') { - icon = '🔄'; - } else if (status === 'IDLE') { - icon = '▶️'; - } - - window.document.title = prependBeta(`n8n - ${icon} ${workflow}`); - }; - - const titleReset = () => { - window.document.title = prependBeta('n8n - Workflow Automation'); - }; - - return { - titleSet, - titleReset, - }; -} diff --git a/packages/editor-ui/src/composables/useWorkflowHelpers.ts b/packages/editor-ui/src/composables/useWorkflowHelpers.ts index 77d8abeb14a45..6848ee7ff4718 100644 --- a/packages/editor-ui/src/composables/useWorkflowHelpers.ts +++ b/packages/editor-ui/src/composables/useWorkflowHelpers.ts @@ -37,6 +37,7 @@ import type { IWorkflowDataUpdate, IWorkflowDb, TargetItem, + WorkflowTitleStatus, XYPosition, } from '@/Interface'; @@ -57,6 +58,7 @@ import { getSourceItems } from '@/utils/pairedItemUtils'; import { v4 as uuid } from 'uuid'; import { useSettingsStore } from '@/stores/settings.store'; import { getCredentialTypeName, isCredentialOnlyNodeType } from '@/utils/credentialOnlyNodes'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import { useExternalHooks } from '@/composables/useExternalHooks'; import { useCanvasStore } from '@/stores/canvas.store'; import { useSourceControlStore } from '@/stores/sourceControl.store'; @@ -458,6 +460,17 @@ export function useWorkflowHelpers(options: { router: ReturnType { + let icon = '⚠️'; + if (status === 'EXECUTING') { + icon = '🔄'; + } else if (status === 'IDLE') { + icon = '▶️'; + } + documentTitle.set(`${icon} ${workflowName}`); + }; function getNodeTypesMaxCount() { const nodes = workflowsStore.allNodes; @@ -1172,6 +1185,7 @@ export function useWorkflowHelpers(options: { router: ReturnType { ExpressionEvaluatorProxy.setEvaluator(settings.value.expressions.evaluator); - // Re-compute title since settings are now available - useTitleChange().titleReset(); - initialized.value = true; } catch (e) { showToast({ diff --git a/packages/editor-ui/src/utils/htmlUtils.ts b/packages/editor-ui/src/utils/htmlUtils.ts index 1fea9b7833ddd..cf4bb85c6c0f3 100644 --- a/packages/editor-ui/src/utils/htmlUtils.ts +++ b/packages/editor-ui/src/utils/htmlUtils.ts @@ -48,10 +48,6 @@ export const sanitizeIfString = (message: T): string | T => { return message; }; -export function setPageTitle(title: string) { - window.document.title = title; -} - export function convertRemToPixels(rem: string) { return parseInt(rem, 10) * parseFloat(getComputedStyle(document.documentElement).fontSize); } diff --git a/packages/editor-ui/src/views/CredentialsView.vue b/packages/editor-ui/src/views/CredentialsView.vue index 31040e2b391ed..c544f998aaecd 100644 --- a/packages/editor-ui/src/views/CredentialsView.vue +++ b/packages/editor-ui/src/views/CredentialsView.vue @@ -18,6 +18,7 @@ import ProjectTabs from '@/components/Projects/ProjectTabs.vue'; import useEnvironmentsStore from '@/stores/environments.ee.store'; import { useSettingsStore } from '@/stores/settings.store'; import { getResourcePermissions } from '@/permissions'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; export default defineComponent({ name: 'CredentialsView', @@ -35,6 +36,7 @@ export default defineComponent({ }, sourceControlStoreUnsubscribe: () => {}, loading: false, + documentTitle: useDocumentTitle(), }; }, computed: { @@ -86,6 +88,7 @@ export default defineComponent({ }, }, mounted() { + this.documentTitle.set(this.$locale.baseText('credentials.heading')); this.sourceControlStoreUnsubscribe = this.sourceControlStore.$onAction(({ name, after }) => { if (name === 'pullWorkfolder' && after) { after(() => { diff --git a/packages/editor-ui/src/views/ExecutionsView.vue b/packages/editor-ui/src/views/ExecutionsView.vue index bdb37c46accd1..7ac9205ce79ce 100644 --- a/packages/editor-ui/src/views/ExecutionsView.vue +++ b/packages/editor-ui/src/views/ExecutionsView.vue @@ -1,13 +1,13 @@ diff --git a/packages/editor-ui/src/views/SettingsApiView.vue b/packages/editor-ui/src/views/SettingsApiView.vue index 0278063718384..2bb67d04400ac 100644 --- a/packages/editor-ui/src/views/SettingsApiView.vue +++ b/packages/editor-ui/src/views/SettingsApiView.vue @@ -3,6 +3,7 @@ import { defineComponent } from 'vue'; import type { ApiKey, IUser } from '@/Interface'; import { useToast } from '@/composables/useToast'; import { useMessage } from '@/composables/useMessage'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import CopyInput from '@/components/CopyInput.vue'; import { mapStores } from 'pinia'; @@ -23,6 +24,7 @@ export default defineComponent({ ...useToast(), ...useMessage(), ...useUIStore(), + documentTitle: useDocumentTitle(), }; }, data() { @@ -35,6 +37,7 @@ export default defineComponent({ }; }, mounted() { + this.documentTitle.set(this.$locale.baseText('settings.api')); if (!this.isPublicApiEnabled) return; void this.getApiKeys(); diff --git a/packages/editor-ui/src/views/SettingsCommunityNodesView.vue b/packages/editor-ui/src/views/SettingsCommunityNodesView.vue index 0c0ceafbb1357..79ce24a46878b 100644 --- a/packages/editor-ui/src/views/SettingsCommunityNodesView.vue +++ b/packages/editor-ui/src/views/SettingsCommunityNodesView.vue @@ -5,6 +5,7 @@ import { } from '@/constants'; import CommunityPackageCard from '@/components/CommunityPackageCard.vue'; import { useToast } from '@/composables/useToast'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import type { PublicInstalledPackage } from 'n8n-workflow'; import { useCommunityNodesStore } from '@/stores/communityNodes.store'; @@ -31,6 +32,7 @@ const externalHooks = useExternalHooks(); const i18n = useI18n(); const telemetry = useTelemetry(); const toast = useToast(); +const documentTitle = useDocumentTitle(); const communityNodesStore = useCommunityNodesStore(); const uiStore = useUIStore(); @@ -85,6 +87,7 @@ onBeforeMount(() => { }); onMounted(async () => { + documentTitle.set(i18n.baseText('settings.communityNodes')); try { loading.value = true; await communityNodesStore.fetchInstalledPackages(); diff --git a/packages/editor-ui/src/views/SettingsExternalSecrets.vue b/packages/editor-ui/src/views/SettingsExternalSecrets.vue index a8fe89fb6c561..30aaebeae1b7e 100644 --- a/packages/editor-ui/src/views/SettingsExternalSecrets.vue +++ b/packages/editor-ui/src/views/SettingsExternalSecrets.vue @@ -2,6 +2,7 @@ import { useUIStore } from '@/stores/ui.store'; import { useI18n } from '@/composables/useI18n'; import { useToast } from '@/composables/useToast'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import { useExternalSecretsStore } from '@/stores/externalSecrets.ee.store'; import { computed, onMounted } from 'vue'; import ExternalSecretsProviderCard from '@/components/ExternalSecretsProviderCard.ee.vue'; @@ -11,6 +12,7 @@ const i18n = useI18n(); const uiStore = useUIStore(); const externalSecretsStore = useExternalSecretsStore(); const toast = useToast(); +const documentTitle = useDocumentTitle(); const sortedProviders = computed(() => { return ([...externalSecretsStore.providers] as ExternalSecretsProvider[]).sort((a, b) => { @@ -19,6 +21,7 @@ const sortedProviders = computed(() => { }); onMounted(() => { + documentTitle.set(i18n.baseText('settings.externalSecrets.title')); if (!externalSecretsStore.isEnterpriseExternalSecretsEnabled) return; try { void externalSecretsStore.fetchAllSecrets(); diff --git a/packages/editor-ui/src/views/SettingsLdapView.vue b/packages/editor-ui/src/views/SettingsLdapView.vue index b0c497e817d97..41227c854e122 100644 --- a/packages/editor-ui/src/views/SettingsLdapView.vue +++ b/packages/editor-ui/src/views/SettingsLdapView.vue @@ -5,6 +5,7 @@ import { capitalizeFirstLetter } from '@/utils/htmlUtils'; import { convertToDisplayDate } from '@/utils/typesUtils'; import { useToast } from '@/composables/useToast'; import { useMessage } from '@/composables/useMessage'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import type { ILdapConfig, ILdapSyncData, @@ -65,6 +66,7 @@ type CellClassStyleMethodParams = { const toast = useToast(); const i18n = useI18n(); const message = useMessage(); +const documentTitle = useDocumentTitle(); const settingsStore = useSettingsStore(); const uiStore = useUIStore(); @@ -585,6 +587,7 @@ const reloadLdapSynchronizations = async () => { }; onMounted(async () => { + documentTitle.set(i18n.baseText('settings.ldap')); if (!isLDAPFeatureEnabled.value) return; await getLdapConfig(); }); diff --git a/packages/editor-ui/src/views/SettingsLogStreamingView.vue b/packages/editor-ui/src/views/SettingsLogStreamingView.vue index b39f5fd6429f3..fae75b2604c21 100644 --- a/packages/editor-ui/src/views/SettingsLogStreamingView.vue +++ b/packages/editor-ui/src/views/SettingsLogStreamingView.vue @@ -13,6 +13,7 @@ import type { MessageEventBusDestinationOptions } from 'n8n-workflow'; import { deepCopy, defaultMessageEventBusDestinationOptions } from 'n8n-workflow'; import EventDestinationCard from '@/components/SettingsLogStreaming/EventDestinationCard.ee.vue'; import { createEventBus } from 'n8n-design-system/utils'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; export default defineComponent({ name: 'SettingsLogStreamingView', @@ -26,9 +27,11 @@ export default defineComponent({ destinations: Array, disableLicense: false, allDestinations: [] as MessageEventBusDestinationOptions[], + documentTitle: useDocumentTitle(), }; }, async mounted() { + this.documentTitle.set(this.$locale.baseText('settings.log-streaming.heading')); if (!this.isLicensed) return; // Prepare credentialsStore so modals can pick up credentials diff --git a/packages/editor-ui/src/views/SettingsPersonalView.vue b/packages/editor-ui/src/views/SettingsPersonalView.vue index 0ddbe6e5a5469..af1c92519dc4d 100644 --- a/packages/editor-ui/src/views/SettingsPersonalView.vue +++ b/packages/editor-ui/src/views/SettingsPersonalView.vue @@ -2,6 +2,7 @@ import { ref, computed, onMounted, onBeforeUnmount } from 'vue'; import { useI18n } from '@/composables/useI18n'; import { useToast } from '@/composables/useToast'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import type { IFormInputs, IUser, ThemeOption } from '@/Interface'; import { CHANGE_PASSWORD_MODAL_KEY, @@ -28,6 +29,7 @@ type UserBasicDetailsWithMfa = UserBasicDetailsForm & { const i18n = useI18n(); const { showToast, showError } = useToast(); +const documentTitle = useDocumentTitle(); const hasAnyBasicInfoChanges = ref(false); const formInputs = ref(null); @@ -80,6 +82,7 @@ const hasAnyChanges = computed(() => { }); onMounted(() => { + documentTitle.set(i18n.baseText('settings.personal.personalSettings')); formInputs.value = [ { name: 'firstName', diff --git a/packages/editor-ui/src/views/SettingsSourceControl.vue b/packages/editor-ui/src/views/SettingsSourceControl.vue index 23505092dad4c..3928556104a8a 100644 --- a/packages/editor-ui/src/views/SettingsSourceControl.vue +++ b/packages/editor-ui/src/views/SettingsSourceControl.vue @@ -8,6 +8,7 @@ import { useToast } from '@/composables/useToast'; import { useLoadingService } from '@/composables/useLoadingService'; import { useI18n } from '@/composables/useI18n'; import { useMessage } from '@/composables/useMessage'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import CopyInput from '@/components/CopyInput.vue'; import type { TupleToUnion } from '@/utils/typeHelpers'; import type { SshKeyTypes } from '@/Interface'; @@ -17,6 +18,7 @@ const sourceControlStore = useSourceControlStore(); const uiStore = useUIStore(); const toast = useToast(); const message = useMessage(); +const documentTitle = useDocumentTitle(); const loadingService = useLoadingService(); const isConnected = ref(false); @@ -112,6 +114,7 @@ const initialize = async () => { }; onMounted(async () => { + documentTitle.set(locale.baseText('settings.sourceControl.title')); if (!sourceControlStore.isEnterpriseSourceControlEnabled) return; await initialize(); }); diff --git a/packages/editor-ui/src/views/SettingsSso.vue b/packages/editor-ui/src/views/SettingsSso.vue index a73733392bbee..a9043c41aef35 100644 --- a/packages/editor-ui/src/views/SettingsSso.vue +++ b/packages/editor-ui/src/views/SettingsSso.vue @@ -7,6 +7,7 @@ import { useI18n } from '@/composables/useI18n'; import { useMessage } from '@/composables/useMessage'; import { useToast } from '@/composables/useToast'; import { useTelemetry } from '@/composables/useTelemetry'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import { useRootStore } from '@/stores/root.store'; const IdentityProviderSettingsType = { @@ -21,6 +22,7 @@ const ssoStore = useSSOStore(); const uiStore = useUIStore(); const message = useMessage(); const toast = useToast(); +const documentTitle = useDocumentTitle(); const ssoActivatedLabel = computed(() => ssoStore.isSamlLoginEnabled @@ -144,6 +146,7 @@ const isToggleSsoDisabled = computed(() => { }); onMounted(async () => { + documentTitle.set(i18n.baseText('settings.sso.title')); if (!ssoStore.isEnterpriseSamlEnabled) { return; } diff --git a/packages/editor-ui/src/views/SettingsUsageAndPlan.vue b/packages/editor-ui/src/views/SettingsUsageAndPlan.vue index 85ac34af9a90a..f562bf7bb9e20 100644 --- a/packages/editor-ui/src/views/SettingsUsageAndPlan.vue +++ b/packages/editor-ui/src/views/SettingsUsageAndPlan.vue @@ -7,6 +7,7 @@ import { telemetry } from '@/plugins/telemetry'; import { i18n as locale } from '@/plugins/i18n'; import { useUIStore } from '@/stores/ui.store'; import { useToast } from '@/composables/useToast'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import { hasPermission } from '@/utils/rbac/permissions'; const usageStore = useUsageStore(); @@ -14,6 +15,7 @@ const route = useRoute(); const router = useRouter(); const uiStore = useUIStore(); const toast = useToast(); +const documentTitle = useDocumentTitle(); const queryParamCallback = ref( `callback=${encodeURIComponent(`${window.location.origin}${window.location.pathname}`)}`, @@ -64,6 +66,7 @@ const onLicenseActivation = async () => { }; onMounted(async () => { + documentTitle.set(locale.baseText('settings.usageAndPlan.title')); usageStore.setLoading(true); if (route.query.key) { try { diff --git a/packages/editor-ui/src/views/SettingsUsersView.vue b/packages/editor-ui/src/views/SettingsUsersView.vue index a0fea3b4b8ded..e29bb3e755223 100644 --- a/packages/editor-ui/src/views/SettingsUsersView.vue +++ b/packages/editor-ui/src/views/SettingsUsersView.vue @@ -12,6 +12,7 @@ import { useClipboard } from '@/composables/useClipboard'; import type { UpdateGlobalRolePayload } from '@/api/users'; import { computed, onMounted } from 'vue'; import { useI18n } from '@/composables/useI18n'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; const clipboard = useClipboard(); const { showToast, showError } = useToast(); @@ -20,6 +21,7 @@ const settingsStore = useSettingsStore(); const uiStore = useUIStore(); const usersStore = useUsersStore(); const ssoStore = useSSOStore(); +const documentTitle = useDocumentTitle(); const i18n = useI18n(); @@ -28,6 +30,8 @@ const showUMSetupWarning = computed(() => { }); onMounted(async () => { + documentTitle.set(i18n.baseText('settings.users')); + if (!showUMSetupWarning.value) { await usersStore.fetchUsers(); } diff --git a/packages/editor-ui/src/views/TemplatesCollectionView.vue b/packages/editor-ui/src/views/TemplatesCollectionView.vue index 535f6649672a6..5432a8ed54604 100644 --- a/packages/editor-ui/src/views/TemplatesCollectionView.vue +++ b/packages/editor-ui/src/views/TemplatesCollectionView.vue @@ -13,7 +13,7 @@ import { useNodeTypesStore } from '@/stores/nodeTypes.store'; import { isFullTemplatesCollection } from '@/utils/templates/typeGuards'; import { useRoute, useRouter } from 'vue-router'; import { useTelemetry } from '@/composables/useTelemetry'; -import { setPageTitle } from '@/utils/htmlUtils'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; import { useI18n } from '@/composables/useI18n'; const externalHooks = useExternalHooks(); @@ -25,6 +25,7 @@ const route = useRoute(); const router = useRouter(); const telemetry = useTelemetry(); const i18n = useI18n(); +const documentTitle = useDocumentTitle(); const loading = ref(true); const notFoundError = ref(false); @@ -89,9 +90,9 @@ watch( () => collection.value, () => { if (collection.value && 'full' in collection.value && collection.value.full) { - setPageTitle(`n8n - Template collection: ${collection.value.name}`); + documentTitle.set(`Template collection: ${collection.value.name}`); } else { - setPageTitle('n8n - Templates'); + documentTitle.set('Templates'); } }, ); diff --git a/packages/editor-ui/src/views/TemplatesSearchView.vue b/packages/editor-ui/src/views/TemplatesSearchView.vue index 86c2b789f4d05..abb30a3586682 100644 --- a/packages/editor-ui/src/views/TemplatesSearchView.vue +++ b/packages/editor-ui/src/views/TemplatesSearchView.vue @@ -13,7 +13,6 @@ import type { ITemplatesCategory, } from '@/Interface'; import type { IDataObject } from 'n8n-workflow'; -import { setPageTitle } from '@/utils/htmlUtils'; import { CREATOR_HUB_URL, VIEWS } from '@/constants'; import { useSettingsStore } from '@/stores/settings.store'; import { useUsersStore } from '@/stores/users.store'; @@ -22,6 +21,7 @@ import { useUIStore } from '@/stores/ui.store'; import { useToast } from '@/composables/useToast'; import { usePostHog } from '@/stores/posthog.store'; import { useDebounce } from '@/composables/useDebounce'; +import { useDocumentTitle } from '@/composables/useDocumentTitle'; interface ISearchEvent { search_string: string; @@ -55,6 +55,7 @@ export default defineComponent({ return { callDebounced, ...useToast(), + documentTitle: useDocumentTitle(), }; }, data() { @@ -116,7 +117,7 @@ export default defineComponent({ }, }, async mounted() { - setPageTitle('n8n - Templates'); + this.documentTitle.set('Templates'); await this.loadCategories(); void this.loadWorkflowsAndCollections(true); void this.usersStore.showPersonalizationSurvey(); diff --git a/packages/editor-ui/src/views/TemplatesWorkflowView.vue b/packages/editor-ui/src/views/TemplatesWorkflowView.vue index 1bfb25af3c8ef..8b86da9e6c35f 100644 --- a/packages/editor-ui/src/views/TemplatesWorkflowView.vue +++ b/packages/editor-ui/src/views/TemplatesWorkflowView.vue @@ -1,6 +1,5 @@