mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-09 22:24:05 -08:00
feat: Add onboarding flow (#7212)
Github issue / Community forum post (link here to close automatically):
This commit is contained in:
parent
60c152dc72
commit
01e9340621
34
cypress/e2e/29-templates.cy.ts
Normal file
34
cypress/e2e/29-templates.cy.ts
Normal file
|
@ -0,0 +1,34 @@
|
|||
import { TemplatesPage } from '../pages/templates';
|
||||
import { WorkflowPage } from '../pages/workflow';
|
||||
|
||||
import OnboardingWorkflow from '../fixtures/Onboarding_workflow.json';
|
||||
|
||||
const templatesPage = new TemplatesPage();
|
||||
const workflowPage = new WorkflowPage();
|
||||
|
||||
describe('Templates', () => {
|
||||
it('can open onboarding flow', () => {
|
||||
templatesPage.actions.openOnboardingFlow(1234, OnboardingWorkflow.name, OnboardingWorkflow);
|
||||
cy.url().then(($url) => {
|
||||
expect($url).to.match(/.*\/workflow\/.*?onboardingId=1234$/);
|
||||
})
|
||||
|
||||
workflowPage.actions.shouldHaveWorkflowName(`Demo: ${name}`);
|
||||
|
||||
workflowPage.getters.canvasNodes().should('have.length', 4);
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
workflowPage.getters.canvasNodes().first().should('have.descendants', '.node-pin-data-icon');
|
||||
});
|
||||
|
||||
it('can import template', () => {
|
||||
templatesPage.actions.importTemplate(1234, OnboardingWorkflow.name, OnboardingWorkflow);
|
||||
|
||||
cy.url().then(($url) => {
|
||||
expect($url).to.include('/workflow/new?templateId=1234');
|
||||
});
|
||||
|
||||
workflowPage.getters.canvasNodes().should('have.length', 4);
|
||||
workflowPage.getters.stickies().should('have.length', 1);
|
||||
workflowPage.actions.shouldHaveWorkflowName(OnboardingWorkflow.name);
|
||||
});
|
||||
});
|
1020
cypress/fixtures/Onboarding_workflow.json
Normal file
1020
cypress/fixtures/Onboarding_workflow.json
Normal file
File diff suppressed because it is too large
Load diff
50
cypress/pages/templates.ts
Normal file
50
cypress/pages/templates.ts
Normal file
|
@ -0,0 +1,50 @@
|
|||
import { BasePage } from './base';
|
||||
import { WorkflowPage } from './workflow';
|
||||
|
||||
const workflowPage = new WorkflowPage();
|
||||
export class TemplatesPage extends BasePage {
|
||||
url = '/templates';
|
||||
|
||||
getters = {
|
||||
};
|
||||
|
||||
actions = {
|
||||
openOnboardingFlow: (id: number, name: string , workflow: object) => {
|
||||
const apiResponse = {
|
||||
id,
|
||||
name,
|
||||
workflow,
|
||||
};
|
||||
cy.intercept('POST', '/rest/workflows').as('createWorkflow');
|
||||
cy.intercept('GET', `https://api.n8n.io/api/workflows/templates/${id}`, {
|
||||
statusCode: 200,
|
||||
body: apiResponse,
|
||||
}).as('getTemplate');
|
||||
cy.intercept('GET', 'rest/workflows/**').as('getWorkflow');
|
||||
|
||||
cy.visit(`/workflows/onboarding/${id}`);
|
||||
|
||||
cy.wait('@getTemplate');
|
||||
cy.wait(['@createWorkflow', '@getWorkflow']);
|
||||
},
|
||||
|
||||
importTemplate: (id: number, name: string, workflow: object) => {
|
||||
const apiResponse = {
|
||||
id,
|
||||
name,
|
||||
workflow,
|
||||
};
|
||||
cy.intercept('GET', `https://api.n8n.io/api/workflows/templates/${id}`, {
|
||||
statusCode: 200,
|
||||
body: apiResponse,
|
||||
}).as('getTemplate');
|
||||
cy.intercept('GET', 'rest/workflows/**').as('getWorkflow');
|
||||
|
||||
cy.visit(`/workflows/templates/${id}`);
|
||||
|
||||
cy.wait('@getTemplate');
|
||||
cy.wait( '@getWorkflow');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -304,5 +304,11 @@ export class WorkflowPage extends BasePage {
|
|||
editSticky: (content: string) => {
|
||||
this.getters.stickies().dblclick().find('textarea').clear().type(content).type('{esc}');
|
||||
},
|
||||
shouldHaveWorkflowName: (name: string) => {
|
||||
this.getters
|
||||
.workflowNameInputContainer()
|
||||
.invoke('attr', 'title')
|
||||
.should('include', name);
|
||||
},
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
import { Length } from 'class-validator';
|
||||
|
||||
import { IConnections, IDataObject, IWorkflowSettings } from 'n8n-workflow';
|
||||
import { IConnections, IDataObject, IWorkflowSettings, WorkflowFEMeta } from 'n8n-workflow';
|
||||
import type { IBinaryKeyData, INode, IPairedItemData } from 'n8n-workflow';
|
||||
|
||||
import { Column, Entity, Index, JoinColumn, JoinTable, ManyToMany, OneToMany } from 'typeorm';
|
||||
|
@ -46,6 +46,13 @@ export class WorkflowEntity extends WithTimestampsAndStringId implements IWorkfl
|
|||
})
|
||||
staticData?: IDataObject;
|
||||
|
||||
@Column({
|
||||
type: jsonColumnType,
|
||||
nullable: true,
|
||||
transformer: objectRetriever,
|
||||
})
|
||||
meta?: WorkflowFEMeta;
|
||||
|
||||
@ManyToMany('TagEntity', 'workflows')
|
||||
@JoinTable({
|
||||
name: 'workflows_tags', // table name for the junction table of this relation
|
||||
|
|
|
@ -0,0 +1,11 @@
|
|||
import type { MigrationContext, ReversibleMigration } from '@db/types';
|
||||
|
||||
export class AddWorkflowMetadata1695128658538 implements ReversibleMigration {
|
||||
async up({ schemaBuilder: { addColumns, column } }: MigrationContext) {
|
||||
await addColumns('workflow_entity', [column('meta').json]);
|
||||
}
|
||||
|
||||
async down({ schemaBuilder: { dropColumns } }: MigrationContext) {
|
||||
await dropColumns('workflow_entity', ['meta']);
|
||||
}
|
||||
}
|
|
@ -48,6 +48,7 @@ import { AddMfaColumns1690000000030 } from './../common/1690000000040-AddMfaColu
|
|||
import { CreateWorkflowHistoryTable1692967111175 } from '../common/1692967111175-CreateWorkflowHistoryTable';
|
||||
import { DisallowOrphanExecutions1693554410387 } from '../common/1693554410387-DisallowOrphanExecutions';
|
||||
import { ExecutionSoftDelete1693491613982 } from '../common/1693491613982-ExecutionSoftDelete';
|
||||
import { AddWorkflowMetadata1695128658538 } from '../common/1695128658538-AddWorkflowMetadata';
|
||||
|
||||
export const mysqlMigrations: Migration[] = [
|
||||
InitialMigration1588157391238,
|
||||
|
@ -99,4 +100,5 @@ export const mysqlMigrations: Migration[] = [
|
|||
CreateWorkflowHistoryTable1692967111175,
|
||||
DisallowOrphanExecutions1693554410387,
|
||||
ExecutionSoftDelete1693491613982,
|
||||
AddWorkflowMetadata1695128658538,
|
||||
];
|
||||
|
|
|
@ -46,6 +46,7 @@ import { AddMfaColumns1690000000030 } from './../common/1690000000040-AddMfaColu
|
|||
import { CreateWorkflowHistoryTable1692967111175 } from '../common/1692967111175-CreateWorkflowHistoryTable';
|
||||
import { DisallowOrphanExecutions1693554410387 } from '../common/1693554410387-DisallowOrphanExecutions';
|
||||
import { ExecutionSoftDelete1693491613982 } from '../common/1693491613982-ExecutionSoftDelete';
|
||||
import { AddWorkflowMetadata1695128658538 } from '../common/1695128658538-AddWorkflowMetadata';
|
||||
|
||||
export const postgresMigrations: Migration[] = [
|
||||
InitialMigration1587669153312,
|
||||
|
@ -95,4 +96,5 @@ export const postgresMigrations: Migration[] = [
|
|||
CreateWorkflowHistoryTable1692967111175,
|
||||
DisallowOrphanExecutions1693554410387,
|
||||
ExecutionSoftDelete1693491613982,
|
||||
AddWorkflowMetadata1695128658538,
|
||||
];
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
import { AddWorkflowMetadata1695128658538 as BaseMigration } from '../common/1695128658538-AddWorkflowMetadata';
|
||||
|
||||
export class AddWorkflowMetadata1695128658538 extends BaseMigration {
|
||||
transaction = false as const;
|
||||
}
|
|
@ -45,6 +45,7 @@ import { AddMfaColumns1690000000030 } from './1690000000040-AddMfaColumns';
|
|||
import { CreateWorkflowHistoryTable1692967111175 } from '../common/1692967111175-CreateWorkflowHistoryTable';
|
||||
import { DisallowOrphanExecutions1693554410387 } from '../common/1693554410387-DisallowOrphanExecutions';
|
||||
import { ExecutionSoftDelete1693491613982 } from './1693491613982-ExecutionSoftDelete';
|
||||
import { AddWorkflowMetadata1695128658538 } from '../common/1695128658538-AddWorkflowMetadata';
|
||||
|
||||
const sqliteMigrations: Migration[] = [
|
||||
InitialMigration1588102412422,
|
||||
|
@ -93,6 +94,7 @@ const sqliteMigrations: Migration[] = [
|
|||
CreateWorkflowHistoryTable1692967111175,
|
||||
DisallowOrphanExecutions1693554410387,
|
||||
ExecutionSoftDelete1693491613982,
|
||||
AddWorkflowMetadata1695128658538,
|
||||
];
|
||||
|
||||
export { sqliteMigrations };
|
||||
|
|
|
@ -85,6 +85,7 @@ export declare namespace WorkflowRequest {
|
|||
active: boolean;
|
||||
tags: string[];
|
||||
hash: string;
|
||||
meta: Record<string, unknown>;
|
||||
}>;
|
||||
|
||||
type ManualRunPayload = {
|
||||
|
|
|
@ -214,6 +214,7 @@ export interface IWorkflowDataUpdate {
|
|||
tags?: ITag[] | string[]; // string[] when store or requested, ITag[] from API response
|
||||
pinData?: IPinData;
|
||||
versionId?: string;
|
||||
meta?: WorkflowMetadata;
|
||||
}
|
||||
|
||||
export interface IWorkflowToShare extends IWorkflowDataUpdate {
|
||||
|
@ -225,10 +226,7 @@ export interface IWorkflowToShare extends IWorkflowDataUpdate {
|
|||
export interface IWorkflowTemplate {
|
||||
id: number;
|
||||
name: string;
|
||||
workflow: {
|
||||
nodes: INodeUi[];
|
||||
connections: IConnections;
|
||||
};
|
||||
workflow: Pick<IWorkflowData, 'nodes' | 'connections' | 'settings' | 'pinData'>;
|
||||
}
|
||||
|
||||
export interface INewWorkflowData {
|
||||
|
@ -236,6 +234,10 @@ export interface INewWorkflowData {
|
|||
onboardingFlowEnabled: boolean;
|
||||
}
|
||||
|
||||
export interface WorkflowMetadata {
|
||||
onboardingId?: string;
|
||||
}
|
||||
|
||||
// Almost identical to cli.Interfaces.ts
|
||||
export interface IWorkflowDb {
|
||||
id: string;
|
||||
|
@ -252,6 +254,7 @@ export interface IWorkflowDb {
|
|||
ownedBy?: Partial<IUser>;
|
||||
versionId: string;
|
||||
usedCredentials?: IUsedCredential[];
|
||||
meta?: WorkflowMetadata;
|
||||
}
|
||||
|
||||
// Identical to cli.Interfaces.ts
|
||||
|
|
|
@ -361,6 +361,7 @@ export const enum VIEWS {
|
|||
WORKFLOW = 'NodeViewExisting',
|
||||
DEMO = 'WorkflowDemo',
|
||||
TEMPLATE_IMPORT = 'WorkflowTemplate',
|
||||
WORKFLOW_ONBOARDING = 'WorkflowOnboarding',
|
||||
SIGNIN = 'SigninView',
|
||||
SIGNUP = 'SignupView',
|
||||
SIGNOUT = 'SignoutView',
|
||||
|
|
|
@ -878,8 +878,6 @@ export const workflowHelpers = defineComponent({
|
|||
|
||||
const workflowDataRequest: IWorkflowDataUpdate =
|
||||
data || (await this.getWorkflowDataToSave());
|
||||
// make sure that the new ones are not active
|
||||
workflowDataRequest.active = false;
|
||||
const changedNodes = {} as IDataObject;
|
||||
|
||||
if (resetNodeIds) {
|
||||
|
|
|
@ -1560,6 +1560,7 @@
|
|||
"tagsTableHeader.searchTags": "Search Tags",
|
||||
"tagsView.inUse": "{count} workflow | {count} workflows",
|
||||
"tagsView.notBeingUsed": "Not being used",
|
||||
"onboarding.title": "Demo: {name}",
|
||||
"template.buttons.goBackButton": "Go back",
|
||||
"template.buttons.useThisWorkflowButton": "Use this workflow",
|
||||
"template.details.appsInTheCollection": "This collection features",
|
||||
|
|
|
@ -41,6 +41,7 @@ import SettingsSourceControl from './views/SettingsSourceControl.vue';
|
|||
import SettingsExternalSecrets from './views/SettingsExternalSecrets.vue';
|
||||
import SettingsAuditLogs from './views/SettingsAuditLogs.vue';
|
||||
import WorkflowHistory from '@/views/WorkflowHistory.vue';
|
||||
import WorkflowOnboardingView from '@/views/WorkflowOnboardingView.vue';
|
||||
import { EnterpriseEditionFeature, VIEWS } from '@/constants';
|
||||
|
||||
interface IRouteConfig {
|
||||
|
@ -57,11 +58,11 @@ interface IRouteConfig {
|
|||
};
|
||||
}
|
||||
|
||||
function getTemplatesRedirect() {
|
||||
function getTemplatesRedirect(defaultRedirect: VIEWS[keyof VIEWS]) {
|
||||
const settingsStore = useSettingsStore();
|
||||
const isTemplatesEnabled: boolean = settingsStore.isTemplatesEnabled;
|
||||
if (!isTemplatesEnabled) {
|
||||
return { name: VIEWS.NOT_FOUND };
|
||||
return { name: defaultRedirect || VIEWS.NOT_FOUND };
|
||||
}
|
||||
|
||||
return false;
|
||||
|
@ -334,6 +335,24 @@ export const routes = [
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/workflows/onboarding/:id',
|
||||
name: VIEWS.WORKFLOW_ONBOARDING,
|
||||
components: {
|
||||
default: WorkflowOnboardingView,
|
||||
header: MainHeader,
|
||||
sidebar: MainSidebar,
|
||||
},
|
||||
meta: {
|
||||
templatesEnabled: true,
|
||||
getRedirect: () => getTemplatesRedirect(VIEWS.NEW_WORKFLOW),
|
||||
permissions: {
|
||||
allow: {
|
||||
loginStatus: [LOGIN_STATUS.LoggedIn],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/workflow/new',
|
||||
name: VIEWS.NEW_WORKFLOW,
|
||||
|
|
101
packages/editor-ui/src/stores/__tests__/workflows.spec.ts
Normal file
101
packages/editor-ui/src/stores/__tests__/workflows.spec.ts
Normal file
|
@ -0,0 +1,101 @@
|
|||
import { setActivePinia, createPinia } from 'pinia';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import type { IWorkflowDataUpdate } from '@/Interface';
|
||||
import { makeRestApiRequest } from '@/utils';
|
||||
import { useRootStore } from '../n8nRoot.store';
|
||||
|
||||
vi.mock('@/utils', () => ({
|
||||
makeRestApiRequest: vi.fn(),
|
||||
}));
|
||||
|
||||
const MOCK_WORKFLOW_SIMPLE: IWorkflowDataUpdate = {
|
||||
id: '1',
|
||||
name: 'test',
|
||||
nodes: [
|
||||
{
|
||||
parameters: {
|
||||
path: '21a77783-e050-4e0f-9915-2d2dd5b53cde',
|
||||
options: {},
|
||||
},
|
||||
id: '2dbf9369-2eec-42e7-9b89-37e50af12289',
|
||||
name: 'Webhook',
|
||||
type: 'n8n-nodes-base.webhook',
|
||||
typeVersion: 1,
|
||||
position: [340, 240],
|
||||
webhookId: '21a77783-e050-4e0f-9915-2d2dd5b53cde',
|
||||
},
|
||||
{
|
||||
parameters: {
|
||||
table: 'product',
|
||||
columns: 'name,ean',
|
||||
additionalFields: {},
|
||||
},
|
||||
name: 'Insert Rows1',
|
||||
type: 'n8n-nodes-base.postgres',
|
||||
position: [580, 240],
|
||||
typeVersion: 1,
|
||||
id: 'a10ba62a-8792-437c-87df-0762fa53e157',
|
||||
credentials: {
|
||||
postgres: {
|
||||
id: 'iEFl08xIegmR8xF6',
|
||||
name: 'Postgres account',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
connections: {
|
||||
Webhook: {
|
||||
main: [
|
||||
[
|
||||
{
|
||||
node: 'Insert Rows1',
|
||||
type: 'main',
|
||||
index: 0,
|
||||
},
|
||||
],
|
||||
],
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
describe('worklfows store', () => {
|
||||
beforeEach(() => {
|
||||
setActivePinia(createPinia());
|
||||
});
|
||||
|
||||
describe('createNewWorkflow', () => {
|
||||
beforeEach(() => {
|
||||
vi.resetAllMocks();
|
||||
});
|
||||
|
||||
it('creates new workflow', async () => {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
await workflowsStore.createNewWorkflow(MOCK_WORKFLOW_SIMPLE);
|
||||
|
||||
expect(makeRestApiRequest).toHaveBeenCalledWith(
|
||||
useRootStore().getRestApiContext,
|
||||
'POST',
|
||||
'/workflows',
|
||||
{
|
||||
...MOCK_WORKFLOW_SIMPLE,
|
||||
active: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
|
||||
it('sets active to false', async () => {
|
||||
const workflowsStore = useWorkflowsStore();
|
||||
await workflowsStore.createNewWorkflow({ ...MOCK_WORKFLOW_SIMPLE, active: true });
|
||||
|
||||
expect(makeRestApiRequest).toHaveBeenCalledWith(
|
||||
useRootStore().getRestApiContext,
|
||||
'POST',
|
||||
'/workflows',
|
||||
{
|
||||
...MOCK_WORKFLOW_SIMPLE,
|
||||
active: false,
|
||||
},
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
|
@ -1,6 +1,7 @@
|
|||
import { defineStore } from 'pinia';
|
||||
import { STORES } from '@/constants';
|
||||
import type {
|
||||
INodeUi,
|
||||
ITemplatesCategory,
|
||||
ITemplatesCollection,
|
||||
ITemplatesCollectionFull,
|
||||
|
@ -19,6 +20,7 @@ import {
|
|||
getWorkflows,
|
||||
getWorkflowTemplate,
|
||||
} from '@/api/templates';
|
||||
import { getFixedNodesList } from '@/utils/nodeViewUtils';
|
||||
|
||||
const TEMPLATES_PAGE_SIZE = 10;
|
||||
|
||||
|
@ -332,5 +334,19 @@ export const useTemplatesStore = defineStore(STORES.TEMPLATES, {
|
|||
const versionCli: string = settingsStore.versionCli;
|
||||
return getWorkflowTemplate(apiEndpoint, templateId, { 'n8n-version': versionCli });
|
||||
},
|
||||
|
||||
async getFixedWorkflowTemplate(templateId: string): Promise<IWorkflowTemplate | undefined> {
|
||||
const template = await this.getWorkflowTemplate(templateId);
|
||||
if (template?.workflow?.nodes) {
|
||||
template.workflow.nodes = getFixedNodesList(template.workflow.nodes) as INodeUi[];
|
||||
template.workflow.nodes?.forEach((node) => {
|
||||
if (node.credentials) {
|
||||
delete node.credentials;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
return template;
|
||||
},
|
||||
},
|
||||
});
|
||||
|
|
|
@ -1272,6 +1272,9 @@ export const useWorkflowsStore = defineStore(STORES.WORKFLOWS, {
|
|||
|
||||
// Creates a new workflow
|
||||
async createNewWorkflow(sendData: IWorkflowDataUpdate): Promise<IWorkflowDb> {
|
||||
// make sure that the new ones are not active
|
||||
sendData.active = false;
|
||||
|
||||
const rootStore = useRootStore();
|
||||
return makeRestApiRequest(
|
||||
rootStore.getRestApiContext,
|
||||
|
|
|
@ -886,7 +886,7 @@ export default defineComponent({
|
|||
let data: IWorkflowTemplate | undefined;
|
||||
try {
|
||||
void this.$externalHooks().run('template.requested', { templateId });
|
||||
data = await this.templatesStore.getWorkflowTemplate(templateId);
|
||||
data = await this.templatesStore.getFixedWorkflowTemplate(templateId);
|
||||
|
||||
if (!data) {
|
||||
throw new Error(
|
||||
|
@ -901,14 +901,6 @@ export default defineComponent({
|
|||
return;
|
||||
}
|
||||
|
||||
data.workflow.nodes = NodeViewUtils.getFixedNodesList(data.workflow.nodes) as INodeUi[];
|
||||
|
||||
data.workflow.nodes?.forEach((node) => {
|
||||
if (node.credentials) {
|
||||
delete node.credentials;
|
||||
}
|
||||
});
|
||||
|
||||
this.blankRedirect = true;
|
||||
await this.$router.replace({ name: VIEWS.NEW_WORKFLOW, query: { templateId } });
|
||||
|
||||
|
@ -2635,6 +2627,15 @@ export default defineComponent({
|
|||
this.titleSet(workflow.name, 'IDLE');
|
||||
await this.openWorkflow(workflow);
|
||||
await this.checkAndInitDebugMode();
|
||||
|
||||
if (workflow.meta?.onboardingId) {
|
||||
this.$telemetry.track(
|
||||
`User opened workflow from onboarding template with ID ${workflow.meta.onboardingId}`,
|
||||
{
|
||||
workflow_id: workflow.id,
|
||||
},
|
||||
);
|
||||
}
|
||||
}
|
||||
} else if (this.$route.meta?.nodeView === true) {
|
||||
// Create new workflow
|
||||
|
|
68
packages/editor-ui/src/views/WorkflowOnboardingView.vue
Normal file
68
packages/editor-ui/src/views/WorkflowOnboardingView.vue
Normal file
|
@ -0,0 +1,68 @@
|
|||
<script setup lang="ts">
|
||||
import { useLoadingService, useI18n } from '@/composables';
|
||||
import { VIEWS } from '@/constants';
|
||||
import { useTemplatesStore, useWorkflowsStore } from '@/stores';
|
||||
import { onMounted } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const loadingService = useLoadingService();
|
||||
const templateStore = useTemplatesStore();
|
||||
const workfowStore = useWorkflowsStore();
|
||||
const router = useRouter();
|
||||
const route = useRoute();
|
||||
const i18n = useI18n();
|
||||
|
||||
const openWorkflowTemplate = async (templateId: string) => {
|
||||
try {
|
||||
loadingService.startLoading();
|
||||
const template = await templateStore.getFixedWorkflowTemplate(templateId);
|
||||
if (!template) {
|
||||
throw new Error();
|
||||
}
|
||||
|
||||
const name: string = i18n.baseText('onboarding.title', {
|
||||
interpolate: { name: template.name },
|
||||
});
|
||||
|
||||
const workflow = await workfowStore.createNewWorkflow({
|
||||
name,
|
||||
connections: template.workflow.connections,
|
||||
nodes: template.workflow.nodes,
|
||||
pinData: template.workflow.pinData,
|
||||
settings: template.workflow.settings,
|
||||
meta: {
|
||||
onboardingId: templateId,
|
||||
},
|
||||
});
|
||||
|
||||
await router.replace({
|
||||
name: VIEWS.WORKFLOW,
|
||||
params: { name: workflow.id },
|
||||
query: { onboardingId: templateId },
|
||||
});
|
||||
|
||||
loadingService.stopLoading();
|
||||
} catch (e) {
|
||||
await router.replace({ name: VIEWS.NEW_WORKFLOW });
|
||||
loadingService.stopLoading();
|
||||
|
||||
throw new Error(`Could not load onboarding template ${templateId}`); // sentry reporing
|
||||
}
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
const templateId = route.params.id;
|
||||
if (!templateId || typeof templateId !== 'string') {
|
||||
await router.replace({ name: VIEWS.NEW_WORKFLOW });
|
||||
return;
|
||||
}
|
||||
|
||||
await openWorkflowTemplate(templateId);
|
||||
});
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div></div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module></style>
|
|
@ -1816,6 +1816,10 @@ export interface IWorkflowSettings {
|
|||
executionOrder?: 'v0' | 'v1';
|
||||
}
|
||||
|
||||
export interface WorkflowFEMeta {
|
||||
onboardingId?: string;
|
||||
}
|
||||
|
||||
export interface WorkflowTestData {
|
||||
description: string;
|
||||
input: {
|
||||
|
|
Loading…
Reference in a new issue