mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(editor): Add disabled state with tooltip on project creation buttons if user lacks permission (#13867)
This commit is contained in:
committed by
GitHub
parent
c7bcdc544d
commit
e33d0d7466
@@ -294,6 +294,7 @@ const {
|
||||
createCredentialsAppendSlotName,
|
||||
projectsLimitReachedMessage,
|
||||
upgradeLabel,
|
||||
hasPermissionToCreateProjects,
|
||||
} = useGlobalEntityCreation();
|
||||
onClickOutside(createBtn as Ref<VueInstance>, () => {
|
||||
createBtn.value?.close();
|
||||
@@ -385,7 +386,14 @@ onClickOutside(createBtn as Ref<VueInstance>, () => {
|
||||
placement="right"
|
||||
:content="projectsLimitReachedMessage"
|
||||
>
|
||||
<N8nIcon
|
||||
v-if="!hasPermissionToCreateProjects"
|
||||
style="margin-left: auto; margin-right: 5px"
|
||||
icon="lock"
|
||||
size="xsmall"
|
||||
/>
|
||||
<N8nButton
|
||||
v-else
|
||||
:size="'mini'"
|
||||
style="margin-left: auto"
|
||||
type="tertiary"
|
||||
|
||||
@@ -195,4 +195,22 @@ describe('ProjectsNavigation', () => {
|
||||
expect(getByTestId('project-plus-button')).toBeVisible();
|
||||
expect(getByTestId('add-first-project-button')).toBeVisible();
|
||||
});
|
||||
|
||||
it('should show project plus button and add first project button in disabled state if user does not have permission', async () => {
|
||||
projectsStore.teamProjectsLimit = -1;
|
||||
projectsStore.hasPermissionToCreateProjects = false;
|
||||
|
||||
const { getByTestId } = renderComponent({
|
||||
props: {
|
||||
collapsed: false,
|
||||
},
|
||||
});
|
||||
const plusButton = getByTestId('project-plus-button');
|
||||
const addFirstProjectButton = getByTestId('add-first-project-button');
|
||||
|
||||
expect(plusButton).toBeVisible();
|
||||
expect(plusButton).toBeDisabled();
|
||||
expect(addFirstProjectButton).toBeVisible();
|
||||
expect(addFirstProjectButton).toBeDisabled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -83,15 +83,21 @@ const showAddFirstProject = computed(
|
||||
bold
|
||||
>
|
||||
<span>{{ locale.baseText('projects.menu.title') }}</span>
|
||||
<N8nButton
|
||||
v-if="projectsStore.canCreateProjects"
|
||||
icon="plus"
|
||||
text
|
||||
data-test-id="project-plus-button"
|
||||
:disabled="isCreatingProject"
|
||||
:class="$style.plusBtn"
|
||||
@click="globalEntityCreation.createProject"
|
||||
/>
|
||||
<N8nTooltip
|
||||
placement="right"
|
||||
:disabled="projectsStore.hasPermissionToCreateProjects"
|
||||
:content="locale.baseText('projects.create.permissionDenied')"
|
||||
>
|
||||
<N8nButton
|
||||
v-if="projectsStore.canCreateProjects"
|
||||
icon="plus"
|
||||
text
|
||||
data-test-id="project-plus-button"
|
||||
:disabled="isCreatingProject || !projectsStore.hasPermissionToCreateProjects"
|
||||
:class="$style.plusBtn"
|
||||
@click="globalEntityCreation.createProject"
|
||||
/>
|
||||
</N8nTooltip>
|
||||
</N8nText>
|
||||
<ElMenu
|
||||
v-if="projectsStore.isTeamProjectFeatureEnabled || isFoldersFeatureEnabled"
|
||||
@@ -118,22 +124,28 @@ const showAddFirstProject = computed(
|
||||
data-test-id="project-menu-item"
|
||||
/>
|
||||
</ElMenu>
|
||||
<N8nButton
|
||||
v-if="showAddFirstProject"
|
||||
:class="[
|
||||
$style.addFirstProjectBtn,
|
||||
{
|
||||
[$style.collapsed]: props.collapsed,
|
||||
},
|
||||
]"
|
||||
:disabled="isCreatingProject"
|
||||
type="secondary"
|
||||
icon="plus"
|
||||
data-test-id="add-first-project-button"
|
||||
@click="globalEntityCreation.createProject"
|
||||
<N8nTooltip
|
||||
placement="right"
|
||||
:disabled="projectsStore.hasPermissionToCreateProjects"
|
||||
:content="locale.baseText('projects.create.permissionDenied')"
|
||||
>
|
||||
{{ locale.baseText('projects.menu.addFirstProject') }}
|
||||
</N8nButton>
|
||||
<N8nButton
|
||||
v-if="showAddFirstProject"
|
||||
:class="[
|
||||
$style.addFirstProjectBtn,
|
||||
{
|
||||
[$style.collapsed]: props.collapsed,
|
||||
},
|
||||
]"
|
||||
:disabled="isCreatingProject || !projectsStore.hasPermissionToCreateProjects"
|
||||
type="secondary"
|
||||
icon="plus"
|
||||
data-test-id="add-first-project-button"
|
||||
@click="globalEntityCreation.createProject"
|
||||
>
|
||||
{{ locale.baseText('projects.menu.addFirstProject') }}
|
||||
</N8nButton>
|
||||
</N8nTooltip>
|
||||
<hr v-if="projectsStore.isTeamProjectFeatureEnabled" class="mb-m" />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -99,6 +99,17 @@ describe('useGlobalEntityCreation', () => {
|
||||
expect(menu.value[0].submenu?.length).toBe(4);
|
||||
expect(menu.value[1].submenu?.length).toBe(4);
|
||||
});
|
||||
|
||||
it('disables project creation item if user has no rbac permission', () => {
|
||||
const projectsStore = mockedStore(useProjectsStore);
|
||||
projectsStore.canCreateProjects = true;
|
||||
projectsStore.isTeamProjectFeatureEnabled = true;
|
||||
projectsStore.hasPermissionToCreateProjects = false;
|
||||
|
||||
const { menu, projectsLimitReachedMessage } = useGlobalEntityCreation();
|
||||
expect(menu.value[2].disabled).toBeTruthy();
|
||||
expect(projectsLimitReachedMessage.value).toContain('Your current role does not allow you');
|
||||
});
|
||||
});
|
||||
|
||||
describe('handleSelect()', () => {
|
||||
@@ -115,6 +126,7 @@ describe('useGlobalEntityCreation', () => {
|
||||
const projectsStore = mockedStore(useProjectsStore);
|
||||
projectsStore.isTeamProjectFeatureEnabled = true;
|
||||
projectsStore.canCreateProjects = true;
|
||||
projectsStore.hasPermissionToCreateProjects = true;
|
||||
projectsStore.createProject.mockResolvedValueOnce({ name: 'test', id: '1' } as Project);
|
||||
|
||||
const { handleSelect } = useGlobalEntityCreation();
|
||||
@@ -132,6 +144,7 @@ describe('useGlobalEntityCreation', () => {
|
||||
const projectsStore = mockedStore(useProjectsStore);
|
||||
projectsStore.isTeamProjectFeatureEnabled = true;
|
||||
projectsStore.canCreateProjects = true;
|
||||
projectsStore.hasPermissionToCreateProjects = true;
|
||||
projectsStore.createProject.mockRejectedValueOnce(new Error('error'));
|
||||
|
||||
const { handleSelect } = useGlobalEntityCreation();
|
||||
@@ -162,6 +175,7 @@ describe('useGlobalEntityCreation', () => {
|
||||
const projectsStore = mockedStore(useProjectsStore);
|
||||
projectsStore.isTeamProjectFeatureEnabled = true;
|
||||
projectsStore.teamProjectsLimit = 10;
|
||||
projectsStore.hasPermissionToCreateProjects = true;
|
||||
|
||||
settingsStore.isCloudDeployment = true;
|
||||
const { projectsLimitReachedMessage, upgradeLabel } = useGlobalEntityCreation();
|
||||
|
||||
@@ -162,7 +162,7 @@ export const useGlobalEntityCreation = () => {
|
||||
{
|
||||
id: CREATE_PROJECT_ID,
|
||||
title: 'Project',
|
||||
disabled: !projectsStore.canCreateProjects,
|
||||
disabled: !projectsStore.canCreateProjects || !projectsStore.hasPermissionToCreateProjects,
|
||||
},
|
||||
];
|
||||
});
|
||||
@@ -192,7 +192,7 @@ export const useGlobalEntityCreation = () => {
|
||||
const handleSelect = (id: string) => {
|
||||
if (id !== CREATE_PROJECT_ID) return;
|
||||
|
||||
if (projectsStore.canCreateProjects) {
|
||||
if (projectsStore.canCreateProjects && projectsStore.hasPermissionToCreateProjects) {
|
||||
void createProject();
|
||||
return;
|
||||
}
|
||||
@@ -215,6 +215,10 @@ export const useGlobalEntityCreation = () => {
|
||||
return i18n.baseText('projects.create.limitReached.self');
|
||||
}
|
||||
|
||||
if (!projectsStore.hasPermissionToCreateProjects) {
|
||||
return i18n.baseText('projects.create.permissionDenied');
|
||||
}
|
||||
|
||||
return i18n.baseText('projects.create.limitReached', {
|
||||
adjustToNumber: projectsStore.teamProjectsLimit,
|
||||
interpolate: {
|
||||
@@ -226,6 +230,7 @@ export const useGlobalEntityCreation = () => {
|
||||
const createProjectAppendSlotName = computed(() => `item.append.${CREATE_PROJECT_ID}`);
|
||||
const createWorkflowsAppendSlotName = computed(() => `item.append.${WORKFLOWS_MENU_ID}`);
|
||||
const createCredentialsAppendSlotName = computed(() => `item.append.${CREDENTIALS_MENU_ID}`);
|
||||
const hasPermissionToCreateProjects = projectsStore.hasPermissionToCreateProjects;
|
||||
|
||||
const upgradeLabel = computed(() => {
|
||||
if (settingsStore.isCloudDeployment) {
|
||||
@@ -246,6 +251,7 @@ export const useGlobalEntityCreation = () => {
|
||||
createWorkflowsAppendSlotName,
|
||||
createCredentialsAppendSlotName,
|
||||
projectsLimitReachedMessage,
|
||||
hasPermissionToCreateProjects,
|
||||
upgradeLabel,
|
||||
createProject,
|
||||
isCreatingProject,
|
||||
|
||||
@@ -2689,6 +2689,7 @@
|
||||
"projects.create.limitReached.cloud": "You have reached the {planName} plan limit of {limit}. Upgrade your plan to unlock more projects.",
|
||||
"projects.create.limitReached.self": "Upgrade to unlock projects for more granular control over sharing, access and organisation of workflows",
|
||||
"projects.create.limitReached.link": "View plans",
|
||||
"projects.create.permissionDenied": "Your current role does not allow you to create projects",
|
||||
"projects.move.resource.modal.title": "Choose a project or user to move this {resourceTypeLabel} to",
|
||||
"projects.move.resource.modal.message": "\"{resourceName}\" is currently {inPersonalProject}{inTeamProject}",
|
||||
"projects.move.resource.modal.message.team": "in the \"{resourceHomeProjectName}\" project.",
|
||||
|
||||
Reference in New Issue
Block a user