fix(core): Add optional context parameter to track creation source for workflows, credentials, and projects (#18736)

Co-authored-by: r00gm <raul00gm@gmail.com>
This commit is contained in:
Csaba Tuncsik
2025-08-27 10:50:53 +02:00
committed by GitHub
parent e665cbf278
commit 98bde4f478
20 changed files with 173 additions and 22 deletions

View File

@@ -52,6 +52,7 @@ import {
import { isCredentialModalState, isValidCredentialResponse } from '@/utils/typeGuards';
import { useI18n } from '@n8n/i18n';
import { useElementSize } from '@vueuse/core';
import { useRouter } from 'vue-router';
type Props = {
modalName: string;
@@ -75,6 +76,7 @@ const toast = useToast();
const message = useMessage();
const i18n = useI18n();
const telemetry = useTelemetry();
const router = useRouter();
const activeTab = ref('connection');
const authError = ref('');
@@ -801,7 +803,16 @@ async function createCredential(
let credential;
try {
credential = await credentialsStore.createNewCredential(credentialDetails, project?.id);
credential = await credentialsStore.createNewCredential(
credentialDetails,
project?.id,
router.currentRoute.value.query.uiContext?.toString(),
);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const { uiContext, ...rest } = router.currentRoute.value.query;
void router.replace({ query: rest });
hasUnsavedChanges.value = false;
const { title, message } = createToastMessagingForNewCredentials(project);

View File

@@ -266,13 +266,15 @@ describe('ProjectHeader', () => {
await userEvent.click(getByTestId('action-credential'));
expect(mockPush).toHaveBeenCalledWith({
name: VIEWS.PROJECTS_CREDENTIALS,
params: {
projectId: project.id,
credentialId: 'create',
},
});
expect(mockPush).toHaveBeenCalledWith(
expect.objectContaining({
name: VIEWS.PROJECTS_CREDENTIALS,
params: {
projectId: project.id,
credentialId: 'create',
},
}),
);
});
});

View File

@@ -162,6 +162,37 @@ const showProjectIcon = computed(() => {
);
});
function isCredentialsListView(routeName: string) {
const CREDENTIAL_VIEWS: string[] = [
VIEWS.PROJECTS_CREDENTIALS,
VIEWS.CREDENTIALS,
VIEWS.SHARED_CREDENTIALS,
];
return CREDENTIAL_VIEWS.includes(routeName);
}
function isWorkflowListView(routeName: string) {
const WORKFLOWS_VIEWS: string[] = [
VIEWS.PROJECTS_WORKFLOWS,
VIEWS.WORKFLOWS,
VIEWS.SHARED_WORKFLOWS,
VIEWS.PROJECTS_FOLDERS,
];
return WORKFLOWS_VIEWS.includes(routeName);
}
function getUIContext(routeName: string) {
if (isCredentialsListView(routeName)) {
return 'credentials_list';
} else if (isWorkflowListView(routeName)) {
return 'workflow_list';
} else {
return;
}
}
const actions: Record<ActionTypes, (projectId: string) => void> = {
[ACTION_TYPES.WORKFLOW]: (projectId: string) => {
void router.push({
@@ -169,6 +200,7 @@ const actions: Record<ActionTypes, (projectId: string) => void> = {
query: {
projectId,
parentFolderId: route.params.folderId as string,
uiContext: getUIContext(route.name?.toString() ?? ''),
},
});
},
@@ -179,6 +211,9 @@ const actions: Record<ActionTypes, (projectId: string) => void> = {
projectId,
credentialId: 'create',
},
query: {
uiContext: getUIContext(route.name?.toString() ?? ''),
},
});
},
[ACTION_TYPES.FOLDER]: () => {

View File

@@ -1,14 +1,14 @@
<script lang="ts" setup>
import { computed, onBeforeMount } from 'vue';
import type { IMenuItem } from '@n8n/design-system/types';
import { useI18n } from '@n8n/i18n';
import { useGlobalEntityCreation } from '@/composables/useGlobalEntityCreation';
import { VIEWS } from '@/constants';
import { useProjectsStore } from '@/stores/projects.store';
import type { ProjectListItem } from '@/types/projects.types';
import { useGlobalEntityCreation } from '@/composables/useGlobalEntityCreation';
import { useSettingsStore } from '@/stores/settings.store';
import { useUsersStore } from '@/stores/users.store';
import type { ProjectListItem } from '@/types/projects.types';
import type { IMenuItem } from '@n8n/design-system/types';
import { useI18n } from '@n8n/i18n';
import { ElMenu } from 'element-plus';
import { computed, onBeforeMount } from 'vue';
type Props = {
collapsed: boolean;
@@ -28,7 +28,7 @@ const isCreatingProject = computed(() => globalEntityCreation.isCreatingProject.
const displayProjects = computed(() => globalEntityCreation.displayProjects.value);
const isFoldersFeatureEnabled = computed(() => settingsStore.isFoldersFeatureEnabled);
const hasMultipleVerifiedUsers = computed(
() => usersStore.allUsers.filter((user) => user.isPendingUser === false).length > 1,
() => usersStore.allUsers.filter((user) => !user.isPendingUser).length > 1,
);
const home = computed<IMenuItem>(() => ({
@@ -140,7 +140,7 @@ onBeforeMount(async () => {
data-test-id="project-plus-button"
:disabled="isCreatingProject || !projectsStore.hasPermissionToCreateProjects"
:class="$style.plusBtn"
@click="globalEntityCreation.createProject"
@click="globalEntityCreation.createProject('add_icon')"
/>
</N8nTooltip>
</N8nText>