diff --git a/frontend/src/lib/components/FlowBuilder.svelte b/frontend/src/lib/components/FlowBuilder.svelte index c06c8649c5..308eb93c7e 100644 --- a/frontend/src/lib/components/FlowBuilder.svelte +++ b/frontend/src/lib/components/FlowBuilder.svelte @@ -58,6 +58,7 @@ import FlowBuilderTutorials from './FlowBuilderTutorials.svelte' import FlowTutorials from './FlowTutorials.svelte' + import { ignoredTutorials } from './tutorials/ignoredTutorials' export let initialPath: string = '' export let selectedId: string | undefined @@ -858,7 +859,7 @@ if (tutorial) { flowTutorials?.runTutorialById(tutorial) - } else if ($tutorialsToDo.includes(0)) { + } else if ($tutorialsToDo.includes(0) && !$ignoredTutorials.includes(0)) { flowTutorials?.runTutorialById('action') } } diff --git a/frontend/src/lib/components/FlowBuilderTutorials.svelte b/frontend/src/lib/components/FlowBuilderTutorials.svelte index 633cdc8a5c..4817426a50 100644 --- a/frontend/src/lib/components/FlowBuilderTutorials.svelte +++ b/frontend/src/lib/components/FlowBuilderTutorials.svelte @@ -29,28 +29,24 @@ on:click={() => flowTutorials?.runTutorialById('action')} label="Simple flow tutorial" index={0} - id="flow-builder-tutorial-action" /> flowTutorials?.runTutorialById('forloop')} label="For loops tutorial" index={1} - id="flow-builder-tutorial-forloops" /> flowTutorials?.runTutorialById('branchone')} label="Branch one tutorial" index={2} - id="flow-builder-tutorial-branchone" /> flowTutorials?.runTutorialById('branchall')} label="Branch all tutorial" index={3} - id="flow-builder-tutorial-branchall" />
@@ -117,6 +113,6 @@ } .driver-popover { - @apply p-6 bg-surface; + @apply p-6 bg-surface max-w-2xl; } diff --git a/frontend/src/lib/components/FlowTutorials.svelte b/frontend/src/lib/components/FlowTutorials.svelte index 0717d74a65..709916fb59 100644 --- a/frontend/src/lib/components/FlowTutorials.svelte +++ b/frontend/src/lib/components/FlowTutorials.svelte @@ -11,7 +11,6 @@ let flowBranchAll: FlowBranchAll | undefined = undefined export function runTutorialById(id: string) { - console.log(id, flowBranchOne) if (id === 'forloop') { flowBuilderTutorialsForLoop?.runTutorial() } else if (id === 'branchone') { diff --git a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte index fb36290c3a..12afad7c5e 100644 --- a/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte +++ b/frontend/src/lib/components/flows/map/FlowModuleSchemaMap.svelte @@ -26,6 +26,7 @@ import FlowTutorials from '$lib/components/FlowTutorials.svelte' import { tainted } from '$lib/components/tutorials/utils' + import { ignoredTutorials } from '$lib/components/tutorials/ignoredTutorials' export let modules: FlowModule[] | undefined export let sidebarSize: number | undefined = undefined @@ -140,6 +141,18 @@ const { currentStepStore: copilotCurrentStepStore } = getContext('FlowCopilotContext') || {} + + function shouldRunTutorial(tutorialName: string, name: string, index: number) { + const svg = document.getElementsByClassName('driver-overlay driver-overlay-animated') + const isTainted = tainted($flowStore) + return ( + $tutorialsToDo.includes(index) && + name == tutorialName && + svg.length === 0 && + !isTainted && + !$ignoredTutorials.includes(index) + ) + } @@ -212,28 +225,11 @@ } }} on:insert={async ({ detail }) => { - const svg = document.getElementsByClassName('driver-overlay driver-overlay-animated') - const isTainted = tainted($flowStore) - if ( - $tutorialsToDo.includes(1) && - detail.detail == 'forloop' && - svg.length === 0 && - !isTainted - ) { + if (shouldRunTutorial('forloop', detail.detail, 1)) { flowTutorials?.runTutorialById('forloop') - } else if ( - $tutorialsToDo.includes(2) && - detail.detail == 'branchone' && - svg.length === 0 && - !isTainted - ) { + } else if (shouldRunTutorial('branchone', detail.detail, 2)) { flowTutorials?.runTutorialById('branchone') - } else if ( - $tutorialsToDo.includes(3) && - detail.detail == 'branchall' && - svg.length === 0 && - !isTainted - ) { + } else if (shouldRunTutorial('branchall', detail.detail, 3)) { flowTutorials?.runTutorialById('branchall') } else { if (detail.modules) { diff --git a/frontend/src/lib/components/tutorials/FlowBranchAll.svelte b/frontend/src/lib/components/tutorials/FlowBranchAll.svelte index 5bfd921c61..fcdfb276db 100644 --- a/frontend/src/lib/components/tutorials/FlowBranchAll.svelte +++ b/frontend/src/lib/components/tutorials/FlowBranchAll.svelte @@ -1,221 +1,179 @@ + + [ + { + popover: { + title: 'Welcome to the Windmil Flow editor', + description: + 'Learn how to build our first branch to be executed on a condition. You can use arrow keys to navigate' + } + }, + { + element: '#flow-editor-add-step-0', + popover: { + title: 'Add a step', + description: 'Click here to add a step to your flow', + onNextClick: () => { + triggerAddFlowStep(0) + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + popover: { + title: 'Steps kind', + description: "Choose the kind of step you want to add. Let's start with a simple action" + }, + element: '#flow-editor-insert-module' + }, + + { + popover: { + title: 'Insert Branch all', + description: "Let's pick branch all", + onNextClick: () => { + selectFlowStepKind(6) + + setTimeout(() => { + driver.moveNext() + }) + } + }, + element: '#flow-editor-insert-module > div > button:nth-child(6)' + }, + + { + element: '#add-branch-button', + popover: { + title: 'Add branch', + description: 'Click here to add a branch', + onNextClick: () => { + clickButtonBySelector('#add-branch-button') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-step-input', + popover: { + title: 'Branch all', + description: 'All branches will be executed, and the result will be gathered in an array', + + onNextClick: () => { + if ($flowStore.value.modules[0].value.type === 'branchall') { + $flowStore.value.modules[0].value = { + type: 'branchall', + branches: [ + { + modules: [ + { + id: 'b', + value: { + type: 'rawscript', + content: + '// import * as wmill from "npm:windmill-client@1"\n\nexport async function main(x: string) {\n return "Hello"\n}\n', + language: RawScript.language.DENO, + input_transforms: { + x: { + type: 'static' + } + } + } + } + ], + skip_failure: false + }, + { + summary: '', + modules: [ + { + id: 'c', + value: { + type: 'rawscript', + content: + 'package inner\n\nfunc main() (interface{}, error) {\n\treturn "World", nil\n}\n', + language: RawScript.language.GO, + input_transforms: {} + } + } + ], + skip_failure: false + } + ], + parallel: false + } + } + + $flowStore = $flowStore + dispatch('reload') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-test-flow', + popover: { + title: 'Test your flow', + description: 'We can now test our flow', + onNextClick: () => { + clickButtonBySelector('#flow-editor-test-flow') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-test-flow-drawer', + popover: { + title: 'Test your flow', + description: 'Finally we can test our flow, and view the results!', + onNextClick: () => { + clickButtonBySelector('#flow-editor-test-flow-drawer') + + setTimeout(() => { + driver.moveNext() + updateProgress(3) + }) + } + } + } + ]} +/> diff --git a/frontend/src/lib/components/tutorials/FlowBranchOne.svelte b/frontend/src/lib/components/tutorials/FlowBranchOne.svelte index 1708140451..56ac250674 100644 --- a/frontend/src/lib/components/tutorials/FlowBranchOne.svelte +++ b/frontend/src/lib/components/tutorials/FlowBranchOne.svelte @@ -1,313 +1,271 @@ - const branchOneTutorial = driver({ - showProgress: true, - allowClose: true, - onPopoverRender: (popover, { config, state }) => { - if (state.activeIndex == 0) { - const skipThisButton = document.createElement('button') - skipThisButton.innerText = 'Skip this tutorials' - skipThisButton.addEventListener('click', () => { - updateProgress(2) + [ + { + popover: { + title: 'Welcome to the Windmil Flow editor', + description: + 'Learn how to build our first branch to be executed on a condition. You can use arrow keys to navigate' + } + }, - branchOneTutorial.destroy() + { + popover: { + title: 'Flows inputs', + description: 'Flows have inputs that can be used in the flow', + onNextClick: () => { + clickButtonBySelector('#flow-editor-virtual-Input') + + setTimeout(() => { + driver.moveNext() }) - skipThisButton.setAttribute( - 'class', - 'border px-2 py-1 !text-xs font-normal rounded-md hover:bg-surface-hover transition-all flex items-center' - ) - - const skipAllButton = document.createElement('button') - skipAllButton.innerText = 'Skip all tutorials' - skipAllButton.addEventListener('click', () => { - dispatch('skipAll') - branchOneTutorial.destroy() - }) - - skipAllButton.setAttribute( - 'class', - 'border px-2 py-1 !text-xs font-normal rounded-md hover:bg-surface-hover transition-all flex items-center' - ) - - const popoverDescription = document.querySelector('#driver-popover-description') - - const div = document.createElement('div') - - div.setAttribute('class', 'flex flex-row gap-2 justify-between w-full pt-2') - - if (popoverDescription) { - div.appendChild(skipAllButton) - div.appendChild(skipThisButton) - - popoverDescription.appendChild(div) - } } }, - steps: [ - { - popover: { - title: 'Welcome to the Windmil Flow editor', - description: - 'Learn how to build our first branch to be executed on a condition. You can use arrow keys to navigate' + element: '#svelvet-Input' + }, + { + element: '#flow-editor-add-property', + popover: { + title: 'Add a property', + description: 'Click here to add a property to your schema', + onNextClick: () => { + clickButtonBySelector('#flow-editor-add-property') + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#schema-modal-name', + popover: { + title: 'Name your property', + description: 'Give a name to your property. Here we will call it condition', + onNextClick: () => { + setInputBySelector('#schema-modal-name', 'condition') + driver.moveNext() + } + } + }, + { + element: '#schema-modal-type-boolean', + popover: { + title: 'Property type', + description: 'Choose the type of your property. Here we will choose boolean', + onNextClick: () => { + clickButtonBySelector('#schema-modal-type-boolean') + driver.moveNext() + } + } + }, + { + element: '#schema-modal-save', + popover: { + title: 'Save your property', + description: 'Click here to save your property', + onNextClick: () => { + clickButtonBySelector('#schema-modal-save') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-add-step-0', + popover: { + title: 'Branch one', + description: 'Windmill supports branches, let us add one', + onNextClick: () => { + triggerAddFlowStep(0) + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + popover: { + title: 'Steps kind', + description: 'Choose the kind of step you want to add.' + }, + element: '#flow-editor-insert-module' + }, + { + popover: { + title: 'Insert Branch one', + description: "Let's pick branch one", + onNextClick: () => { + selectFlowStepKind(5) + + setTimeout(() => { + driver.moveNext() + }) + } + }, + element: '#flow-editor-insert-module > div > button:nth-child(5)' + }, + + { + element: '#add-branch-button', + popover: { + title: 'Add branch', + description: 'Click here to add a branch', + onNextClick: () => { + clickButtonBySelector('#add-branch-button') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-edit-predicate', + popover: { + title: 'Edit predicate', + description: 'Click here to edit the predicate of your branch', + onNextClick: () => { + clickButtonBySelector('#flow-editor-edit-predicate') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '.key', + popover: { + title: 'Connect', + description: 'As we did before, we can connect to the iterator of the loop', + onNextClick: () => { + if ($flowStore.value.modules[0].value.type === 'branchone') { + $flowStore.value.modules[0].value.branches[0].expr = 'flow_input.condition' } - }, - { - popover: { - title: 'Flows inputs', - description: 'Flows have inputs that can be used in the flow', - onNextClick: () => { - clickButtonBySelector('#flow-editor-virtual-Input') + $flowStore = $flowStore + dispatch('reload') - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - }, - element: '#svelvet-Input' - }, - { - element: '#flow-editor-add-property', - popover: { - title: 'Add a property', - description: 'Click here to add a property to your schema', - onNextClick: () => { - clickButtonBySelector('#flow-editor-add-property') - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - } - }, - { - element: '#schema-modal-name', - popover: { - title: 'Name your property', - description: 'Give a name to your property. Here we will call it condition', - onNextClick: () => { - setInputBySelector('#schema-modal-name', 'condition') - branchOneTutorial.moveNext() - } - } - }, - { - element: '#schema-modal-type-boolean', - popover: { - title: 'Property type', - description: 'Choose the type of your property. Here we will choose boolean', - onNextClick: () => { - clickButtonBySelector('#schema-modal-type-boolean') - branchOneTutorial.moveNext() - } - } - }, - { - element: '#schema-modal-save', - popover: { - title: 'Save your property', - description: 'Click here to save your property', - onNextClick: () => { - clickButtonBySelector('#schema-modal-save') + setTimeout(() => { + driver.moveNext() + }) + } + } + }, - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - } - }, + //flow-editor-add-step- - { - element: '#flow-editor-add-step-0', - popover: { - title: 'Branch one', - description: 'Windmill supports branches, let us add one', - onNextClick: () => { - triggerAddFlowStep(0) - - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - } - }, - { - popover: { - title: 'Steps kind', - description: 'Choose the kind of step you want to add.' - }, - element: '#flow-editor-insert-module' - }, - { - popover: { - title: 'Insert Branch one', - description: "Let's pick branch one", - onNextClick: () => { - selectFlowStepKind(5) - - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - }, - element: '#flow-editor-insert-module > div > button:nth-child(5)' - }, - - { - element: '#add-branch-button', - popover: { - title: 'Add branch', - description: 'Click here to add a branch', - onNextClick: () => { - clickButtonBySelector('#add-branch-button') - - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - } - }, - - { - element: '#flow-editor-edit-predicate', - popover: { - title: 'Edit predicate', - description: 'Click here to edit the predicate of your branch', - onNextClick: () => { - clickButtonBySelector('#flow-editor-edit-predicate') - - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - } - }, - - { - element: '.key', - popover: { - title: 'Connect', - description: 'As we did before, we can connect to the iterator of the loop', - onNextClick: () => { - if ($flowStore.value.modules[0].value.type === 'branchone') { - $flowStore.value.modules[0].value.branches[0].expr = 'flow_input.condition' - } - - $flowStore = $flowStore - dispatch('reload') - - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - } - }, - - //flow-editor-add-step- - - { - popover: { - title: 'Branche modules', - description: - 'We can now add modules to each branch, one in typescript and one in python', - onNextClick: () => { - if ($flowStore.value.modules[0].value.type === 'branchone') { - $flowStore.value.modules[0].value = { - type: 'branchone', - branches: [ + { + popover: { + title: 'Branche modules', + description: 'We can now add modules to each branch, one in typescript and one in python', + onNextClick: () => { + if ($flowStore.value.modules[0].value.type === 'branchone') { + $flowStore.value.modules[0].value = { + type: 'branchone', + branches: [ + { + summary: '', + expr: 'flow_input.condition', + modules: [ { - summary: '', - expr: 'flow_input.condition', - modules: [ - { - id: 'c', - value: { - type: 'rawscript', - content: - 'export async function main() {\n return "Entered the condition!";\n}\n', - language: RawScript.language.DENO, - input_transforms: {} - } - } - ] - } - ], - default: [ - { - id: 'b', + id: 'c', value: { type: 'rawscript', - content: '# import wmill\n\n\ndef main():\n return "Default Branch"', - language: RawScript.language.PYTHON3, + content: + 'export async function main() {\n return "Entered the condition!";\n}\n', + language: RawScript.language.DENO, input_transforms: {} } } ] } - } - $flowStore = $flowStore - dispatch('reload') - - setTimeout(() => { - branchOneTutorial.moveNext() - }) + ], + default: [ + { + id: 'b', + value: { + type: 'rawscript', + content: '# import wmill\n\n\ndef main():\n return "Default Branch"', + language: RawScript.language.PYTHON3, + input_transforms: {} + } + } + ] } } - }, + $flowStore = $flowStore + dispatch('reload') - { - element: '#flow-editor-test-flow', - popover: { - title: 'Test your flow', - description: 'We can now test our flow', - onNextClick: () => { - clickButtonBySelector('#flow-editor-test-flow') - - setTimeout(() => { - branchOneTutorial.moveNext() - }) - } - } - }, - - { - element: '#flow-editor-test-flow-drawer', - popover: { - title: 'Test your flow', - description: 'Finally we can test our flow, and view the results!', - onNextClick: () => { - clickButtonBySelector('#flow-editor-test-flow-drawer') - - setTimeout(() => { - branchOneTutorial.moveNext() - updateProgress(2) - }) - } - } + setTimeout(() => { + driver.moveNext() + }) } - ] - }) - branchOneTutorial.drive() - } - + } + }, + + { + element: '#flow-editor-test-flow', + popover: { + title: 'Test your flow', + description: 'We can now test our flow', + onNextClick: () => { + clickButtonBySelector('#flow-editor-test-flow') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-test-flow-drawer', + popover: { + title: 'Test your flow', + description: 'Finally we can test our flow, and view the results!', + onNextClick: () => { + clickButtonBySelector('#flow-editor-test-flow-drawer') + + setTimeout(() => { + driver.moveNext() + updateProgress(2) + }) + } + } + } + ]} +/> diff --git a/frontend/src/lib/components/tutorials/FlowBuilderTutorialSimpleFlow.svelte b/frontend/src/lib/components/tutorials/FlowBuilderTutorialSimpleFlow.svelte index 8e210c62bb..29217c43bf 100644 --- a/frontend/src/lib/components/tutorials/FlowBuilderTutorialSimpleFlow.svelte +++ b/frontend/src/lib/components/tutorials/FlowBuilderTutorialSimpleFlow.svelte @@ -1,6 +1,4 @@ + + [ + { + popover: { + title: 'Welcome to the Windmil Flow editor', + description: + 'Learn how to build powerful flows in a few steps. You can use arrow keys to navigate.' + } + }, + { + popover: { + title: 'Flows inputs', + description: 'Flows have inputs that can be used in the flow', + onNextClick: () => { + clickButtonBySelector('#flow-editor-virtual-Input') + setTimeout(() => { + driver.moveNext() + }) + } + }, + element: '#svelvet-Input' + }, + { + element: '#flow-editor-add-property', + popover: { + title: 'Add a property', + description: 'Click here to add a property to your schema', + onNextClick: () => { + clickButtonBySelector('#flow-editor-add-property') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#schema-modal-name', + popover: { + title: 'Name your property', + description: 'Give a name to your property. Here we will call it firstname', + onNextClick: () => { + setInputBySelector('#schema-modal-name', 'firstname') + + driver.moveNext() + } + } + }, + { + element: '#schema-modal-save', + popover: { + title: 'Save your property', + description: 'Click here to save your property', + onNextClick: () => { + queue.push(JSON.stringify($flowStore)) + + clickButtonBySelector('#schema-modal-save') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#flow-editor-add-step-0', + popover: { + title: 'Add a step', + description: 'Click here to add a step to your flow', + onNextClick: () => { + triggerAddFlowStep(0) + + setTimeout(() => { + driver.moveNext() + }) + }, + + onPrevClick: () => { + driver.moveTo(1) + $flowStore = JSON.parse(queue.pop() || '{}') + dispatch('reload') + } + } + }, + + { + popover: { + title: 'Steps kind', + description: "Choose the kind of step you want to add. Let's start with a simple action" + }, + element: '#flow-editor-insert-module' + }, + { + popover: { + title: 'Pick an action', + description: 'Let’s pick an action to add to your flow', + onNextClick: () => { + selectFlowStepKind(1) + + setTimeout(() => { + driver.moveNext() + }) + } + }, + element: '#flow-editor-insert-module > div > button:nth-child(1)' + }, + { + element: '#flow-editor-flow-inputs', + popover: { + title: 'Action configuration', + description: 'An action can be inlined, imported from your workspace or the Hub.' + } + }, + { + element: '#flow-editor-action-script', + popover: { + title: 'Supported languages', + description: 'Windmill support the following languages/runtimes.' + } + }, + { + element: '#flow-editor-action-script > button:nth-child(1)', + popover: { + title: 'Typescript', + description: "Let's pick an action to add to your flow", + onNextClick: () => { + clickButtonBySelector('#flow-editor-action-script > button > div > button:nth-child(1)') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-editor', + popover: { + title: 'Action editor', + description: 'Windmill provides a full code editor to write your actions' + } + }, + + { + element: '#flow-editor-step-input', + popover: { + title: 'Autogenerated schema', + description: 'The schema and the UI is autogenerated from your code' + } + }, + + { + element: '#flow-editor-plug', + popover: { + title: 'Connect', + description: + 'You can provide static values or connect to other nodes result. Here we will connect to the firstname input', + onNextClick: () => { + clickButtonBySelector('#flow-editor-plug') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '.key', + popover: { + title: 'Connection mode', + description: 'Once you pressed the connect button, you can choose what to connect to.', + onNextClick: () => { + if ($flowStore.value.modules[0].value.type === 'rawscript') { + $flowStore.value.modules[0].value.input_transforms = { + x: { + type: 'javascript', + expr: 'flow_input.firstname' + } + } + } + + $flowStore = $flowStore + dispatch('reload') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-step-input', + popover: { + title: 'Input connected!', + description: 'The input is now connected to the firstname input' + } + }, + + { + element: '#flow-editor-test-flow', + popover: { + title: 'Test your flow', + description: 'We can now test our flow', + onNextClick: () => { + clickButtonBySelector('#flow-editor-test-flow') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-preview-content', + popover: { + title: 'Flow input', + description: 'Let’s provide an input to our flow', + onNextClick: () => { + setInputBySelector('textarea.w-full', 'Hello World!') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#flow-editor-test-flow-drawer', + popover: { + title: 'Test your flow', + description: 'Finally we can test our flow, and view the results!', + onNextClick: () => { + clickButtonBySelector('#flow-editor-test-flow-drawer') + + setTimeout(() => { + driver.moveNext() + + updateProgress(0) + }) + } + } + } + ]} +/> diff --git a/frontend/src/lib/components/tutorials/FlowBuilderTutorialsForLoop.svelte b/frontend/src/lib/components/tutorials/FlowBuilderTutorialsForLoop.svelte index cb3bc60c35..4770c8a135 100644 --- a/frontend/src/lib/components/tutorials/FlowBuilderTutorialsForLoop.svelte +++ b/frontend/src/lib/components/tutorials/FlowBuilderTutorialsForLoop.svelte @@ -1,6 +1,4 @@ + + [ + { + popover: { + title: 'Welcome to the Windmil Flow editor', + description: + 'Learn how to build our first for loop to iterate on. You can use arrow keys to navigate.' + } + }, + + { + popover: { + title: 'Flows inputs', + description: 'Flows have inputs that can be used in the flow', + onNextClick: () => { + clickButtonBySelector('#flow-editor-virtual-Input') + + setTimeout(() => { + driver.moveNext() + }) + } + }, + element: '#svelvet-Input' + }, + { + element: '#flow-editor-add-property', + popover: { + title: 'Add a property', + description: 'Click here to add a property to your schema', + onNextClick: () => { + clickButtonBySelector('#flow-editor-add-property') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#schema-modal-name', + popover: { + title: 'Name your property', + description: 'Give a name to your property. Here we will call it firstname', + onNextClick: () => { + setInputBySelector('#schema-modal-name', 'array') + driver.moveNext() + } + } + }, + { + element: '#schema-modal-type-array', + popover: { + title: 'Property type', + description: 'Choose the type of your property. Here we will choose array', + onNextClick: () => { + clickButtonBySelector('#schema-modal-type-array') + driver.moveNext() + } + } + }, + { + element: '#array-type-narrowing', + popover: { + title: 'Array type narrowing', + description: 'You can narrow the type of your array. Here we will choose numbers', + onNextClick: () => { + selectOptionsBySelector('#array-type-narrowing', 'number') + driver.moveNext() + } + } + }, + { + element: '#schema-modal-save', + popover: { + title: 'Save your property', + description: 'Click here to save your property', + onNextClick: () => { + clickButtonBySelector('#schema-modal-save') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#flow-editor-add-step-0', + popover: { + title: 'Add a step', + description: 'Click here to add a step to your flow', + onNextClick: () => { + triggerAddFlowStep(0) + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + popover: { + title: 'Steps kind', + description: "Choose the kind of step you want to add. Let's start with a simple action" + }, + element: '#flow-editor-insert-module' + }, + + { + popover: { + title: 'Insert loop', + description: "Let's pick forloop", + onNextClick: () => { + selectFlowStepKind(4) + setTimeout(() => { + driver.moveNext() + }) + } + }, + element: '#flow-editor-insert-module > div > button:nth-child(4)' + }, + + { + element: '#flow-editor-iterator-expression', + popover: { + title: 'Iterator expression', + description: + 'The iterator expression is a javascript expression that respresents the array to iterate on. Here we will iterate on the firstname input letter by letter', + onNextClick: () => { + if ($flowStore.value.modules[0].value.type === 'forloopflow') { + if ($flowStore.value.modules[0].value.iterator.type === 'javascript') { + $flowStore.value.modules[0].value.iterator.expr = 'flow_input.array' + } + } + + $flowStore = $flowStore + + dispatch('reload') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-iterator-expression', + popover: { + title: 'Iterator expression', + description: + 'We can refer to the result of previous steps using the results object: results.a', + onNextClick: () => { + if ($flowStore.value.modules[0].value.type === 'forloopflow') { + $flowStore.value.modules[0].value.modules = [ + { + id: 'b', + value: { + type: 'rawscript', + content: 'def main(x: int):\n return x*2', + // @ts-ignore + language: 'python3', + input_transforms: { + x: { + type: 'javascript', + // @ts-ignore + value: '', + expr: '' + } + } + } + } + ] + } + + $flowStateStore['b'] = emptyFlowModuleState() + + $flowStateStore['b'].schema.properties = { + x: { + type: 'number', + description: '', + default: null + } + } + + $flowStore = $flowStore + + dispatch('reload') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#svelvet-c', + popover: { + title: 'Step of the loop', + description: 'We added an action to the loop. Let’s configure it', + onNextClick: () => { + $selectedId = 'b' + $flowStore = $flowStore + + dispatch('reload') + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#flow-editor-editor', + popover: { + title: 'Python step', + description: + 'We can write python code in the editor. In this example we will capitalize the letter' + } + }, + + { + element: '#flow-editor-step-input', + popover: { + title: 'flow editor', + description: 'Description' + } + }, + + { + element: '#flow-editor-plug', + popover: { + title: 'Input configuration', + description: + 'UI is autogenerated from your code. Let’s connect the input to the letter input', + onNextClick: () => { + clickButtonBySelector('#flow-editor-plug') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '.key', + popover: { + title: 'Connect', + description: 'As we did before, we can connect to the iterator of the loop', + onNextClick: () => { + if ( + $flowStore.value.modules[0].value.type === 'forloopflow' && + $flowStore.value.modules[0].value.modules[0].value.type === 'rawscript' + ) { + $flowStore.value.modules[0].value.modules[0].value.input_transforms = { + x: { + type: 'javascript', + expr: 'flow_input.iter.value' + } + } + } + + $flowStore = $flowStore + dispatch('reload') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: '#flow-editor-step-input', + popover: { + title: 'Iterator', + description: + 'Loops expose an iterator object that contains the current value of the loop and the index' + } + }, + { + element: '#flow-editor-test-flow', + popover: { + title: 'Test your flow', + description: 'We can now test our flow', + onNextClick: () => { + clickButtonBySelector('#flow-editor-test-flow') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: 'arg-input-add-item', + popover: { + title: 'Flow input', + description: 'Let’s add an item to our array', + onNextClick: () => { + clickButtonBySelector('#arg-input-add-item') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + { + element: 'arg-input-add-item', + popover: { + title: 'Flow input', + description: 'We can set the value of the item', + onNextClick: () => { + setInputBySelector('#arg-input-number-array', '25') + + setTimeout(() => { + driver.moveNext() + }) + } + } + }, + + { + element: '#flow-editor-test-flow-drawer', + popover: { + title: 'Test your flow', + description: 'Finally we can test our flow, and view the results!', + onNextClick: () => { + clickButtonBySelector('#flow-editor-test-flow-drawer') + setTimeout(() => { + driver.moveNext() + updateProgress(1) + }) + } + } + } + ]} +/> diff --git a/frontend/src/lib/components/tutorials/Tutorial.svelte b/frontend/src/lib/components/tutorials/Tutorial.svelte new file mode 100644 index 0000000000..7142aff24d --- /dev/null +++ b/frontend/src/lib/components/tutorials/Tutorial.svelte @@ -0,0 +1,75 @@ + diff --git a/frontend/src/lib/components/tutorials/TutorialItem.svelte b/frontend/src/lib/components/tutorials/TutorialItem.svelte index dbe234f20f..35e9c01f4c 100644 --- a/frontend/src/lib/components/tutorials/TutorialItem.svelte +++ b/frontend/src/lib/components/tutorials/TutorialItem.svelte @@ -7,24 +7,19 @@ export let label: string export let index: number - export let shouldRenderButton: boolean = true - export let id: string -{#if shouldRenderButton} - -
- {#if $tutorialsToDo.includes(index)} - - {:else} - - {/if} - {label} -
-
-{/if} + +
+ {#if $tutorialsToDo.includes(index)} + + {:else} + + {/if} + {label} +
+
diff --git a/frontend/src/lib/components/tutorials/ignoredTutorials.ts b/frontend/src/lib/components/tutorials/ignoredTutorials.ts new file mode 100644 index 0000000000..7a120b5e0c --- /dev/null +++ b/frontend/src/lib/components/tutorials/ignoredTutorials.ts @@ -0,0 +1,3 @@ +import { writable } from 'svelte/store' + +export const ignoredTutorials = writable([])