Skip to content

Commit

Permalink
fix(editor): Fix project menu item highlight
Browse files Browse the repository at this point in the history
  • Loading branch information
cstuncsik committed May 22, 2024
1 parent 1251291 commit d290d80
Show file tree
Hide file tree
Showing 11 changed files with 93 additions and 31 deletions.
46 changes: 45 additions & 1 deletion cypress/e2e/39-projects.cy.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,23 @@
import { INSTANCE_ADMIN, INSTANCE_MEMBERS } from '../constants';
import { WorkflowsPage, WorkflowPage, CredentialsModal, CredentialsPage } from '../pages';
import {
WorkflowsPage,
WorkflowPage,
CredentialsModal,
CredentialsPage,
WorkflowExecutionsTab,
} from '../pages';
import * as projects from '../composables/projects';

const workflowsPage = new WorkflowsPage();
const workflowPage = new WorkflowPage();
const credentialsPage = new CredentialsPage();
const credentialsModal = new CredentialsModal();
const executionsTab = new WorkflowExecutionsTab();

describe('Projects', () => {
beforeEach(() => {
cy.resetDatabase();
cy.enableFeature('sharing');
cy.enableFeature('advancedPermissions');
cy.enableFeature('projectRole:admin');
cy.enableFeature('projectRole:editor');
Expand Down Expand Up @@ -160,8 +168,26 @@ describe('Projects', () => {
menuItems.filter('[class*=active_]').should('have.length', 1);
menuItems.filter(':contains("Development")[class*=active_]').should('exist');

cy.intercept('GET', '/rest/workflows/*').as('loadWorkflow');
workflowsPage.getters.workflowCards().first().click();

cy.wait('@loadWorkflow');
menuItems = cy.getByTestId('menu-item');

menuItems.filter('[class*=active_]').should('have.length', 1);
menuItems.filter(':contains("Development")[class*=active_]').should('exist');

cy.intercept('GET', '/rest/executions*').as('loadExecutions');
executionsTab.actions.switchToExecutionsTab();

cy.wait('@loadExecutions');
menuItems = cy.getByTestId('menu-item');

menuItems.filter('[class*=active_]').should('have.length', 1);
menuItems.filter(':contains("Development")[class*=active_]').should('exist');

executionsTab.actions.switchToEditorTab();

menuItems = cy.getByTestId('menu-item');

menuItems.filter('[class*=active_]').should('have.length', 1);
Expand All @@ -174,5 +200,23 @@ describe('Projects', () => {

menuItems.filter('[class*=active_]').should('have.length', 1);
menuItems.filter(':contains("Variables")[class*=active_]').should('exist');

projects.getHomeButton().click();
menuItems = cy.getByTestId('menu-item');

menuItems.filter('[class*=active_]').should('have.length', 1);
menuItems.filter(':contains("Home")[class*=active_]').should('exist');

workflowsPage.getters.workflowCards().should('have.length', 2).first().click();

cy.wait('@loadWorkflow');
cy.getByTestId('execute-workflow-button').should('be.visible');

menuItems = cy.getByTestId('menu-item');
menuItems.filter(':contains("Home")[class*=active_]').should('not.exist');

menuItems = cy.getByTestId('menu-item');
menuItems.filter('[class*=active_]').should('have.length', 1);
menuItems.filter(':contains("Development")[class*=active_]').should('exist');
});
});
5 changes: 4 additions & 1 deletion packages/editor-ui/src/__tests__/data/projects.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,14 @@ import type {
ProjectSharingData,
ProjectType,
} from '@/features/projects/projects.types';
import { ProjectTypes } from '@/features/projects/projects.utils';

export const createProjectSharingData = (projectType?: ProjectType): ProjectSharingData => ({
id: faker.string.uuid(),
name: faker.lorem.words({ min: 1, max: 3 }),
type: projectType || 'personal',
type: projectType ?? ProjectTypes.Personal,
createdAt: faker.date.past().toISOString(),
updatedAt: faker.date.recent().toISOString(),
});

export const createProjectListItem = (projectType?: ProjectType): ProjectListItem => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ import type { CredentialScope } from '@n8n/permissions';
import type { EventBus } from 'n8n-design-system/utils';
import { useRolesStore } from '@/stores/roles.store';
import type { RoleMap } from '@/types/roles.types';
import { ProjectTypes } from '@/features/projects/projects.utils';

export default defineComponent({
name: 'CredentialSharing',
Expand Down Expand Up @@ -178,7 +179,7 @@ export default defineComponent({
);
},
isHomeTeamProject(): boolean {
return this.homeProject?.type === 'team';
return this.homeProject?.type === ProjectTypes.Team;
},
numberOfMembersInHomeTeamProject(): number {
return this.teamProject?.relations.length ?? 0;
Expand Down
3 changes: 2 additions & 1 deletion packages/editor-ui/src/components/WorkflowSettings.vue
Original file line number Diff line number Diff line change
Expand Up @@ -384,6 +384,7 @@ import type { WorkflowScope } from '@n8n/permissions';
import { getWorkflowPermissions } from '@/permissions';
import { useExternalHooks } from '@/composables/useExternalHooks';
import { useSourceControlStore } from '@/stores/sourceControl.store';
import { ProjectTypes } from '@/features/projects/projects.utils';
export default defineComponent({
name: 'WorkflowSettings',
Expand Down Expand Up @@ -604,7 +605,7 @@ export default defineComponent({
{
key: 'workflowsFromSameOwner',
value: this.$locale.baseText(
this.workflow.homeProject?.type === 'personal'
this.workflow.homeProject?.type === ProjectTypes.Personal
? 'workflowSettings.callerPolicy.options.workflowsFromPersonalProject'
: 'workflowSettings.callerPolicy.options.workflowsFromTeamProject',
{
Expand Down
3 changes: 2 additions & 1 deletion packages/editor-ui/src/components/WorkflowShareModal.ee.vue
Original file line number Diff line number Diff line change
Expand Up @@ -152,6 +152,7 @@ import type {
} from '@/features/projects/projects.types';
import { useRolesStore } from '@/stores/roles.store';
import type { RoleMap } from '@/types/roles.types';
import { ProjectTypes } from '@/features/projects/projects.utils';

export default defineComponent({
name: 'WorkflowShareModal',
Expand Down Expand Up @@ -238,7 +239,7 @@ export default defineComponent({
);
},
isHomeTeamProject(): boolean {
return this.workflow.homeProject?.type === 'team';
return this.workflow.homeProject?.type === ProjectTypes.Team;
},
numberOfMembersInHomeTeamProject(): number {
return this.teamProject?.relations.length ?? 0;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
<script lang="ts" setup>
import { computed } from 'vue';
import { useI18n } from '@/composables/useI18n';
import { splitName } from '@/features/projects/projects.utils';
import { ProjectTypes, splitName } from '@/features/projects/projects.utils';
import type { ICredentialsResponse, IWorkflowDb } from '@/Interface';
import type { Project } from '@/features/projects/projects.types';
Expand Down Expand Up @@ -29,9 +29,12 @@ const badgeText = computed(() => {
});
const badgeIcon = computed(() => {
if (props.resource.sharedWithProjects?.length && props.resource.homeProject?.type !== 'team') {
if (
props.resource.sharedWithProjects?.length &&
props.resource.homeProject?.type !== ProjectTypes.Team
) {
return 'user-friends';
} else if (props.resource.homeProject?.type === 'team') {
} else if (props.resource.homeProject?.type === ProjectTypes.Team) {
return 'archive';
} else {
return '';
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
<script lang="ts" setup>
import { ref, computed, onMounted, nextTick } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { useRouter } from 'vue-router';
import type { IMenuItem } from 'n8n-design-system/types';
import { useI18n } from '@/composables/useI18n';
import { VIEWS } from '@/constants';
Expand All @@ -16,7 +16,6 @@ type Props = {
const props = defineProps<Props>();
const route = useRoute();
const router = useRouter();
const locale = useI18n();
const toast = useToast();
Expand All @@ -42,20 +41,6 @@ const addProject = computed<IMenuItem>(() => ({
isLoading: isCreatingProject.value,
}));
const activeTab = computed(() => {
let routes = [VIEWS.HOMEPAGE, VIEWS.WORKFLOWS, VIEWS.CREDENTIALS];
if (projectsStore.currentProjectId === undefined) {
routes = [
...routes,
VIEWS.NEW_WORKFLOW,
VIEWS.WORKFLOW_HISTORY,
VIEWS.WORKFLOW,
VIEWS.EXECUTION_HOME,
];
}
return routes.includes(route.name as VIEWS) ? 'home' : undefined;
});
const getProjectMenuItem = (project: ProjectListItem) => ({
id: project.id,
label: project.name,
Expand Down Expand Up @@ -123,7 +108,7 @@ onMounted(async () => {
:item="home"
:compact="props.collapsed"
:handle-select="homeClicked"
:active-tab="activeTab"
:active-tab="projectsStore.projectNavActiveId"
mode="tabs"
data-test-id="project-home-menu-item"
/>
Expand Down
16 changes: 14 additions & 2 deletions packages/editor-ui/src/features/projects/projects.store.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import type {
} from '@/features/projects/projects.types';
import { useSettingsStore } from '@/stores/settings.store';
import { hasPermission } from '@/rbac/permissions';
import { ProjectTypes } from './projects.utils';

export const useProjectsStore = defineStore('projects', () => {
const route = useRoute();
Expand All @@ -36,8 +37,10 @@ export const useProjectsStore = defineStore('projects', () => {
currentProject.value?.id,
);
const isProjectHome = computed(() => route.path.includes('home'));
const personalProjects = computed(() => projects.value.filter((p) => p.type === 'personal'));
const teamProjects = computed(() => projects.value.filter((p) => p.type === 'team'));
const personalProjects = computed(() =>
projects.value.filter((p) => p.type === ProjectTypes.Personal),
);
const teamProjects = computed(() => projects.value.filter((p) => p.type === ProjectTypes.Team));
const teamProjectsLimit = computed(() => settingsStore.settings.enterprise.projects.team.limit);
const teamProjectsAvailable = computed<boolean>(
() => settingsStore.settings.enterprise.projects.team.limit !== 0,
Expand Down Expand Up @@ -124,9 +127,18 @@ export const useProjectsStore = defineStore('projects', () => {
projectNavActiveId.value = null;

if (newRoute?.path?.includes('home')) {
projectNavActiveId.value = 'home';
setCurrentProject(null);
}

if (newRoute?.path?.includes('workflow/')) {
if (currentProjectId.value) {
projectNavActiveId.value = currentProjectId.value;
} else {
projectNavActiveId.value = 'home';
}
}

if (!newRoute?.params?.projectId) {
return;
}
Expand Down
5 changes: 4 additions & 1 deletion packages/editor-ui/src/features/projects/projects.types.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
import type { Scope } from '@n8n/permissions';
import type { IUserResponse } from '@/Interface';
import type { ProjectRole } from '@/types/roles.types';
import type { ProjectTypes } from '@/features/projects/projects.utils';

export type ProjectType = 'personal' | 'team' | 'public';
type ProjectTypeKeys = typeof ProjectTypes;

export type ProjectType = ProjectTypeKeys[keyof ProjectTypeKeys];
export type ProjectRelation = Pick<IUserResponse, 'id' | 'email' | 'firstName' | 'lastName'> & {
role: ProjectRole;
};
Expand Down
6 changes: 6 additions & 0 deletions packages/editor-ui/src/features/projects/projects.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,9 @@ export const splitName = (
}
}
};

export const ProjectTypes = {
Personal: 'personal',
Team: 'team',
Public: 'public',
} as const;
9 changes: 6 additions & 3 deletions packages/editor-ui/src/views/NodeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -395,6 +395,7 @@ import type { ProjectSharingData } from '@/features/projects/projects.types';
import { useAIStore } from '@/stores/ai.store';
import { useStorage } from '@/composables/useStorage';
import { isJSPlumbEndpointElement } from '@/utils/typeGuards';
import { ProjectTypes } from '@/features/projects/projects.utils';

interface AddNodeOptions {
position?: XYPosition;
Expand Down Expand Up @@ -3726,8 +3727,10 @@ export default defineComponent({
await this.openWorkflow(workflow);
await this.checkAndInitDebugMode();

if (this.projectsStore.currentProjectId) {
this.projectsStore.projectNavActiveId = this.projectsStore.currentProjectId;
if (workflow.homeProject?.type === ProjectTypes.Personal) {
this.projectsStore.projectNavActiveId = 'home';
} else {
this.projectsStore.projectNavActiveId = workflow.homeProject?.id ?? null;
}

if (workflow.meta?.onboardingId) {
Expand Down Expand Up @@ -4674,7 +4677,7 @@ export default defineComponent({
async loadCredentials(): Promise<void> {
const workflow = this.workflowsStore.getWorkflowById(this.currentWorkflow);
const projectId =
workflow?.homeProject?.type === 'personal'
workflow?.homeProject?.type === ProjectTypes.Personal
? this.projectsStore.personalProject?.id
: workflow?.homeProject?.id;
await this.credentialsStore.fetchAllCredentials(projectId);
Expand Down

0 comments on commit d290d80

Please sign in to comment.