diff --git a/packages/frontend/@n8n/i18n/src/locales/en.json b/packages/frontend/@n8n/i18n/src/locales/en.json index 4f425b78c40..50d16b6a324 100644 --- a/packages/frontend/@n8n/i18n/src/locales/en.json +++ b/packages/frontend/@n8n/i18n/src/locales/en.json @@ -3062,7 +3062,7 @@ "variables.empty.button": "Add first variable", "variables.empty.button.disabled.tooltip": "Your current role in the project does not allow you to create variables", "variables.empty.notAllowedToCreate.heading": "{name}, start using variables", - "variables.empty.notAllowedToCreate.description": "Ask your n8n instance owner to create the variables you need. Once configured, you can utilize them in your workflows using the syntax $vars.MY_VAR.", + "variables.empty.notAllowedToCreate.description": "Ask project editors or instance admin to create the variables you need. Once configured, you can utilize them in your workflows using the syntax $vars.MY_VAR.", "variables.filters.active": "Some variables may be hidden since filters are applied.", "variables.filters.active.reset": "Remove filters", "variables.noResults": "No variables found", diff --git a/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.test.ts b/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.test.ts index a0f4cf5c5f7..abafd2ca8e0 100644 --- a/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.test.ts +++ b/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.test.ts @@ -14,6 +14,9 @@ import { waitFor, within } from '@testing-library/vue'; import { useSettingsStore } from '@/app/stores/settings.store'; import { useProjectPages } from '@/features/collaboration/projects/composables/useProjectPages'; import { useUIStore } from '@/app/stores/ui.store'; +import { useUsersStore } from '@/features/settings/users/users.store'; +import { mock } from 'vitest-mock-extended'; +import type { IUser } from '@n8n/rest-api-client'; const mockPush = vi.fn(); vi.mock('vue-router', async () => { @@ -76,6 +79,7 @@ let projectsStore: ReturnType>; let settingsStore: ReturnType>; let uiStore: ReturnType>; let projectPages: ReturnType; +let usersStore: ReturnType>; describe('ProjectHeader', () => { beforeEach(() => { @@ -83,6 +87,7 @@ describe('ProjectHeader', () => { route = router.useRoute(); projectsStore = mockedStore(useProjectsStore); settingsStore = mockedStore(useSettingsStore); + usersStore = mockedStore(useUsersStore); uiStore = mockedStore(useUIStore); projectPages = useProjectPages(); @@ -527,5 +532,39 @@ describe('ProjectHeader', () => { }), ); }); + + it('should enable variable create button if project scope allows it', () => { + const project = createTestProject({ + scopes: ['projectVariable:create'], + }); + projectsStore.currentProject = project; + + const { getByTestId } = renderComponent({ props: { mainButton: 'variable' } }); + + expect(getByTestId('add-resource-variable')).toBeInTheDocument(); + expect(getByTestId('add-resource-variable')).toBeEnabled(); + }); + + it('should enable create variable button if global scope allows it', () => { + usersStore.currentUser = mock({ + globalScopes: ['variable:create'], + }); + + const { getByTestId } = renderComponent({ props: { mainButton: 'variable' } }); + + expect(getByTestId('add-resource-variable')).toBeInTheDocument(); + expect(getByTestId('add-resource-variable')).toBeEnabled(); + }); + + it('should not enable variable create button if no scope allows it', () => { + const project = createTestProject({ + scopes: [], + }); + projectsStore.currentProject = project; + + const { queryByTestId } = renderComponent({ props: { mainButton: 'variable' } }); + + expect(queryByTestId('add-resource-variable')).toBeDisabled(); + }); }); }); diff --git a/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.vue b/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.vue index 8c90b5b200b..c03f92df8bc 100644 --- a/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.vue +++ b/packages/frontend/editor-ui/src/features/collaboration/projects/components/ProjectHeader.vue @@ -77,7 +77,10 @@ const projectName = computed(() => { const projectPermissions = computed( () => getResourcePermissions(projectsStore.currentProject?.scopes).project, ); -const globalPermissions = computed( +const projectVariablePermissions = computed( + () => getResourcePermissions(projectsStore.currentProject?.scopes).projectVariable, +); +const globalVariablesPermissions = computed( () => getResourcePermissions(usersStore.currentUser?.globalScopes).variable, ); @@ -164,7 +167,7 @@ const createVariableButton = computed(() => ({ size: 'mini' as const, disabled: sourceControlStore.preferences.branchReadOnly || - (!projectPermissions.value.create && !globalPermissions.value.create), + (!projectVariablePermissions.value.create && !globalVariablesPermissions.value.create), })); const selectedMainButtonType = computed(() => props.mainButton ?? ACTION_TYPES.WORKFLOW);