2019-06-23 03:35:23 -07:00
< template >
< div id = "side-menu" >
2020-01-09 19:53:26 -08:00
< about :dialogVisible ="aboutDialogVisible" @closeDialog ="closeAboutDialog" > < / about >
2019-06-23 03:35:23 -07:00
< executions -list :dialogVisible ="executionsListDialogVisible" @closeDialog ="closeExecutionsListOpenDialog" > < / executions -list >
< credentials -list :dialogVisible ="credentialOpenDialogVisible" @closeDialog ="closeCredentialOpenDialog" > < / credentials -list >
< credentials -edit :dialogVisible ="credentialNewDialogVisible" @closeDialog ="closeCredentialNewDialog" > < / credentials -edit >
< workflow -settings :dialogVisible ="workflowSettingsDialogVisible" @closeDialog ="closeWorkflowSettingsDialog" > < / workflow -settings >
< input type = "file" ref = "importFile" style = "display: none" v -on :change ="handleFileImport()" >
< div class = "side-menu-wrapper" : class = "{expanded: !isCollapsed}" >
2021-05-29 11:31:21 -07:00
< div id = "collapse-change-button" class = "clickable" @click ="toggleCollapse" >
2019-06-23 03:35:23 -07:00
< font -awesome -icon icon = "angle-right" class = "icon" / >
< / div >
< el -menu default -active = " workflow " @select ="handleSelect" :collapse ="isCollapsed" >
< el -menu -item index = "logo" class = "logo-item" >
2020-01-09 19:53:26 -08:00
< a href = "https://n8n.io" target = "_blank" class = "logo" >
2020-07-12 00:28:08 -07:00
< img : src = "basePath + 'n8n-icon-small.png'" class = "icon" alt = "n8n.io" / >
2020-01-09 19:53:26 -08:00
< span class = "logo-text" slot = "title" > n8n . io < / span >
2019-06-23 03:35:23 -07:00
< / a >
< / e l - m e n u - i t e m >
2021-05-04 08:55:39 -07:00
< MenuItemsIterator :items ="sidebarMenuTopItems" :root ="true" / >
2021-04-09 13:44:53 -07:00
2019-09-22 12:59:45 -07:00
< el -submenu index = "workflow" title = "Workflow" >
2019-06-23 03:35:23 -07:00
< template slot = "title" >
< font -awesome -icon icon = "network-wired" / > & nbsp ;
< span slot = "title" class = "item-title-root" > Workflows < / span >
< / template >
< el -menu -item index = "workflow-new" >
< template slot = "title" >
< font -awesome -icon icon = "file" / > & nbsp ;
< span slot = "title" class = "item-title" > New < / span >
< / template >
< / e l - m e n u - i t e m >
< el -menu -item index = "workflow-open" >
< template slot = "title" >
< font -awesome -icon icon = "folder-open" / > & nbsp ;
< span slot = "title" class = "item-title" > Open < / span >
< / template >
< / e l - m e n u - i t e m >
2021-05-29 11:31:21 -07:00
< el -menu -item index = "workflow-save" >
2019-06-23 03:35:23 -07:00
< template slot = "title" >
< font -awesome -icon icon = "save" / >
< span slot = "title" class = "item-title" > Save < / span >
< / template >
< / e l - m e n u - i t e m >
2021-05-29 11:31:21 -07:00
< el -menu -item index = "workflow-duplicate" :disabled ="!currentWorkflow" >
2019-06-23 03:35:23 -07:00
< template slot = "title" >
< font -awesome -icon icon = "copy" / >
2021-05-29 11:31:21 -07:00
< span slot = "title" class = "item-title" > Duplicate < / span >
2019-12-20 14:58:36 -08:00
< / template >
< / e l - m e n u - i t e m >
2019-06-23 03:35:23 -07:00
< el -menu -item index = "workflow-delete" :disabled ="!currentWorkflow" >
< template slot = "title" >
< font -awesome -icon icon = "trash" / >
< span slot = "title" class = "item-title" > Delete < / span >
< / template >
< / e l - m e n u - i t e m >
< el -menu -item index = "workflow-download" >
< template slot = "title" >
< font -awesome -icon icon = "file-download" / >
< span slot = "title" class = "item-title" > Download < / span >
< / template >
< / e l - m e n u - i t e m >
< el -menu -item index = "workflow-import-url" >
< template slot = "title" >
< font -awesome -icon icon = "cloud" / >
< span slot = "title" class = "item-title" > Import from URL < / span >
< / template >
< / e l - m e n u - i t e m >
< el -menu -item index = "workflow-import-file" >
< template slot = "title" >
< font -awesome -icon icon = "hdd" / >
< span slot = "title" class = "item-title" > Import from File < / span >
< / template >
< / e l - m e n u - i t e m >
< el -menu -item index = "workflow-settings" :disabled ="!currentWorkflow" >
< template slot = "title" >
< font -awesome -icon icon = "cog" / >
< span slot = "title" class = "item-title" > Settings < / span >
< / template >
< / e l - m e n u - i t e m >
< / e l - s u b m e n u >
2019-09-22 12:59:45 -07:00
< el -submenu index = "credentials" title = "Credentials" >
2019-06-23 03:35:23 -07:00
< template slot = "title" >
< font -awesome -icon icon = "key" / > & nbsp ;
< span slot = "title" class = "item-title-root" > Credentials < / span >
< / template >
< el -menu -item index = "credentials-new" >
< template slot = "title" >
< font -awesome -icon icon = "file" / >
< span slot = "title" class = "item-title" > New < / span >
< / template >
< / e l - m e n u - i t e m >
< el -menu -item index = "credentials-open" >
< template slot = "title" >
< font -awesome -icon icon = "folder-open" / >
< span slot = "title" class = "item-title" > Open < / span >
< / template >
< / e l - m e n u - i t e m >
< / e l - s u b m e n u >
< el -menu -item index = "executions" >
< font -awesome -icon icon = "tasks" / > & nbsp ;
< span slot = "title" class = "item-title-root" > Executions < / span >
< / e l - m e n u - i t e m >
2019-09-22 12:59:45 -07:00
< el -submenu index = "help" class = "help-menu" title = "Help" >
< template slot = "title" >
< font -awesome -icon icon = "question" / > & nbsp ;
< span slot = "title" class = "item-title-root" > Help < / span >
< / template >
2021-05-04 08:55:39 -07:00
< MenuItemsIterator :items ="helpMenuItems" / >
2020-01-09 19:53:26 -08:00
< el -menu -item index = "help-about" >
< template slot = "title" >
< font -awesome -icon class = "about-icon" icon = "info" / >
< span slot = "title" class = "item-title" > About n8n < / span >
< / template >
< / e l - m e n u - i t e m >
2019-09-22 12:59:45 -07:00
< / e l - s u b m e n u >
2021-05-04 08:55:39 -07:00
< MenuItemsIterator :items ="sidebarMenuBottomItems" :root ="true" / >
2021-04-09 13:44:53 -07:00
2019-06-23 03:35:23 -07:00
< / e l - m e n u >
< / div >
< / div >
< / template >
< script lang = "ts" >
2021-05-04 08:55:39 -07:00
2019-12-29 13:02:21 -08:00
import { MessageBoxInputData } from 'element-ui/types/message-box' ;
2019-06-23 03:35:23 -07:00
import {
IExecutionResponse ,
IExecutionsStopData ,
IWorkflowDataUpdate ,
2021-04-09 13:44:53 -07:00
IMenuItem ,
2019-06-23 03:35:23 -07:00
} from '../Interface' ;
2020-01-09 19:53:26 -08:00
import About from '@/components/About.vue' ;
2019-06-23 03:35:23 -07:00
import CredentialsEdit from '@/components/CredentialsEdit.vue' ;
import CredentialsList from '@/components/CredentialsList.vue' ;
import ExecutionsList from '@/components/ExecutionsList.vue' ;
import WorkflowSettings from '@/components/WorkflowSettings.vue' ;
import { genericHelpers } from '@/components/mixins/genericHelpers' ;
import { restApi } from '@/components/mixins/restApi' ;
import { showMessage } from '@/components/mixins/showMessage' ;
2020-08-25 11:38:09 -07:00
import { titleChange } from '@/components/mixins/titleChange' ;
2019-06-23 03:35:23 -07:00
import { workflowHelpers } from '@/components/mixins/workflowHelpers' ;
import { workflowRun } from '@/components/mixins/workflowRun' ;
import { saveAs } from 'file-saver' ;
import mixins from 'vue-typed-mixins' ;
2021-05-29 11:31:21 -07:00
import { mapGetters } from 'vuex' ;
2021-05-04 08:55:39 -07:00
import MenuItemsIterator from './MainSidebarMenuItemsIterator.vue' ;
const helpMenuItems : IMenuItem [ ] = [
{
id : 'docs' ,
type : 'link' ,
properties : {
href : 'https://docs.n8n.io' ,
title : 'Documentation' ,
icon : 'book' ,
newWindow : true ,
} ,
} ,
{
id : 'forum' ,
type : 'link' ,
properties : {
href : 'https://community.n8n.io' ,
title : 'Forum' ,
icon : 'users' ,
newWindow : true ,
} ,
} ,
{
id : 'examples' ,
type : 'link' ,
properties : {
href : 'https://n8n.io/workflows' ,
title : 'Workflows' ,
icon : 'network-wired' ,
newWindow : true ,
} ,
} ,
] ;
2019-06-23 03:35:23 -07:00
export default mixins (
genericHelpers ,
restApi ,
showMessage ,
2020-08-25 11:38:09 -07:00
titleChange ,
2019-06-23 03:35:23 -07:00
workflowHelpers ,
workflowRun ,
)
. extend ( {
name : 'MainHeader' ,
components : {
2020-01-09 19:53:26 -08:00
About ,
2019-06-23 03:35:23 -07:00
CredentialsEdit ,
CredentialsList ,
ExecutionsList ,
WorkflowSettings ,
2021-05-04 08:55:39 -07:00
MenuItemsIterator ,
2019-06-23 03:35:23 -07:00
} ,
data ( ) {
return {
2020-01-09 19:53:26 -08:00
aboutDialogVisible : false ,
2020-07-12 00:28:08 -07:00
// @ts-ignore
2020-07-14 14:36:05 -07:00
basePath : this . $store . getters . getBaseUrl ,
2019-06-23 03:35:23 -07:00
credentialNewDialogVisible : false ,
credentialOpenDialogVisible : false ,
executionsListDialogVisible : false ,
stopExecutionInProgress : false ,
workflowSettingsDialogVisible : false ,
2021-05-04 08:55:39 -07:00
helpMenuItems ,
2019-06-23 03:35:23 -07:00
} ;
} ,
computed : {
2021-05-29 11:31:21 -07:00
... mapGetters ( 'ui' , {
isCollapsed : 'sidebarMenuCollapsed' ,
} ) ,
2019-06-23 03:35:23 -07:00
exeuctionId ( ) : string | undefined {
return this . $route . params . id ;
} ,
executionFinished ( ) : boolean {
if ( ! this . isExecutionPage ) {
// We are not on an exeuction page so return false
return false ;
}
const fullExecution = this . $store . getters . getWorkflowExecution ;
if ( fullExecution === null ) {
// No exeuction loaded so return also false
return false ;
}
if ( fullExecution . finished === true ) {
return true ;
}
return false ;
} ,
executionWaitingForWebhook ( ) : boolean {
return this . $store . getters . executionWaitingForWebhook ;
} ,
isExecutionPage ( ) : boolean {
if ( [ 'ExecutionById' ] . includes ( this . $route . name as string ) ) {
return true ;
}
return false ;
} ,
isWorkflowActive ( ) : boolean {
return this . $store . getters . isActive ;
} ,
currentWorkflow ( ) : string {
return this . $route . params . name ;
} ,
workflowExecution ( ) : IExecutionResponse | null {
return this . $store . getters . getWorkflowExecution ;
} ,
workflowName ( ) : string {
return this . $store . getters . workflowName ;
} ,
workflowRunning ( ) : boolean {
return this . $store . getters . isActionActive ( 'workflowRunning' ) ;
} ,
2021-04-09 13:44:53 -07:00
sidebarMenuTopItems ( ) : IMenuItem [ ] {
return this . $store . getters . sidebarMenuItems . filter ( ( item : IMenuItem ) => item . position === 'top' ) ;
} ,
sidebarMenuBottomItems ( ) : IMenuItem [ ] {
return this . $store . getters . sidebarMenuItems . filter ( ( item : IMenuItem ) => item . position === 'bottom' ) ;
} ,
2019-06-23 03:35:23 -07:00
} ,
methods : {
2021-05-29 11:31:21 -07:00
toggleCollapse ( ) {
this . $store . commit ( 'ui/toggleSidebarMenuCollapse' ) ;
} ,
2019-06-23 03:35:23 -07:00
clearExecutionData ( ) {
this . $store . commit ( 'setWorkflowExecutionData' , null ) ;
this . updateNodesExecutionIssues ( ) ;
} ,
2020-01-09 19:53:26 -08:00
closeAboutDialog ( ) {
this . aboutDialogVisible = false ;
} ,
2019-06-23 03:35:23 -07:00
closeWorkflowSettingsDialog ( ) {
this . workflowSettingsDialogVisible = false ;
} ,
closeExecutionsListOpenDialog ( ) {
this . executionsListDialogVisible = false ;
} ,
closeCredentialOpenDialog ( ) {
this . credentialOpenDialogVisible = false ;
} ,
closeCredentialNewDialog ( ) {
this . credentialNewDialogVisible = false ;
} ,
2021-05-29 11:31:21 -07:00
openTagManager ( ) {
this . $store . dispatch ( 'ui/openTagsManagerModal' ) ;
} ,
2019-06-23 03:35:23 -07:00
async stopExecution ( ) {
const executionId = this . $store . getters . activeExecutionId ;
if ( executionId === null ) {
return ;
}
try {
this . stopExecutionInProgress = true ;
const stopData : IExecutionsStopData = await this . restApi ( ) . stopCurrentExecution ( executionId ) ;
this . $showMessage ( {
title : 'Execution stopped' ,
message : ` The execution with the id " ${ executionId } " got stopped! ` ,
type : 'success' ,
} ) ;
} catch ( error ) {
this . $showError ( error , 'Problem stopping execution' , 'There was a problem stopping the execuction:' ) ;
}
this . stopExecutionInProgress = false ;
} ,
async openWorkflow ( workflowId : string ) {
// Change to other workflow
this . $router . push ( {
name : 'NodeViewExisting' ,
params : { name : workflowId } ,
} ) ;
2021-05-29 11:31:21 -07:00
this . $store . commit ( 'ui/closeTopModal' ) ;
2019-06-23 03:35:23 -07:00
} ,
async handleFileImport ( ) {
const reader = new FileReader ( ) ;
reader . onload = ( event : ProgressEvent ) => {
const data = ( event . target as FileReader ) . result ;
let worflowData : IWorkflowDataUpdate ;
try {
worflowData = JSON . parse ( data as string ) ;
} catch ( error ) {
this . $showMessage ( {
title : 'Could not import file' ,
message : ` The file does not contain valid JSON data. ` ,
type : 'error' ,
} ) ;
return ;
}
this . $root . $emit ( 'importWorkflowData' , { data : worflowData } ) ;
} ;
const input = this . $refs . importFile as HTMLInputElement ;
if ( input !== null && input . files !== null && input . files . length !== 0 ) {
reader . readAsText ( input ! . files [ 0 ] ! ) ;
}
} ,
async handleSelect ( key : string , keyPath : string ) {
if ( key === 'workflow-open' ) {
2021-05-29 11:31:21 -07:00
this . $store . dispatch ( 'ui/openWorklfowOpenModal' ) ;
2019-06-23 03:35:23 -07:00
} else if ( key === 'workflow-import-file' ) {
( this . $refs . importFile as HTMLInputElement ) . click ( ) ;
} else if ( key === 'workflow-import-url' ) {
try {
const promptResponse = await this . $prompt ( ` Workflow URL: ` , 'Import Workflow from URL:' , {
confirmButtonText : 'Import' ,
cancelButtonText : 'Cancel' ,
inputErrorMessage : 'Invalid URL' ,
inputPattern : /^http[s]?:\/\/.*\.json$/i ,
2019-12-29 13:02:21 -08:00
} ) as MessageBoxInputData ;
2019-06-23 03:35:23 -07:00
this . $root . $emit ( 'importWorkflowUrl' , { url : promptResponse . value } ) ;
} catch ( e ) { }
} else if ( key === 'workflow-delete' ) {
const deleteConfirmed = await this . confirmMessage ( ` Are you sure that you want to delete the workflow " ${ this . workflowName } "? ` , 'Delete Workflow?' , 'warning' , 'Yes, delete!' ) ;
if ( deleteConfirmed === false ) {
return ;
}
let result ;
try {
result = await this . restApi ( ) . deleteWorkflow ( this . currentWorkflow ) ;
} catch ( error ) {
this . $showError ( error , 'Problem deleting the workflow' , 'There was a problem deleting the workflow:' ) ;
return ;
}
2020-08-25 11:37:24 -07:00
// Reset tab title since workflow is deleted.
2020-08-25 11:38:09 -07:00
this . $titleReset ( ) ;
2019-06-23 03:35:23 -07:00
this . $showMessage ( {
title : 'Workflow got deleted' ,
message : ` The workflow " ${ this . workflowName } " got deleted! ` ,
type : 'success' ,
} ) ;
this . $router . push ( { name : 'NodeViewNew' } ) ;
} else if ( key === 'workflow-download' ) {
const workflowData = await this . getWorkflowDataToSave ( ) ;
2021-05-29 11:31:21 -07:00
const { tags , ... data } = workflowData ;
2021-07-10 14:23:19 -07:00
if ( data . id && typeof data . id === 'string' ) {
data . id = parseInt ( data . id , 10 ) ;
}
2021-05-29 11:31:21 -07:00
const blob = new Blob ( [ JSON . stringify ( data , null , 2 ) ] , {
2019-06-23 03:35:23 -07:00
type : 'application/json;charset=utf-8' ,
} ) ;
let workflowName = this . $store . getters . workflowName || 'unsaved_workflow' ;
workflowName = workflowName . replace ( /[^a-z0-9]/gi , '_' ) ;
saveAs ( blob , workflowName + '.json' ) ;
} else if ( key === 'workflow-save' ) {
this . saveCurrentWorkflow ( ) ;
2021-05-29 11:31:21 -07:00
} else if ( key === 'workflow-duplicate' ) {
this . $store . dispatch ( 'ui/openDuplicateModal' ) ;
2020-01-09 19:53:26 -08:00
} else if ( key === 'help-about' ) {
this . aboutDialogVisible = true ;
2019-06-23 03:35:23 -07:00
} else if ( key === 'workflow-settings' ) {
this . workflowSettingsDialogVisible = true ;
} else if ( key === 'workflow-new' ) {
2020-09-01 07:06:08 -07:00
const result = this . $store . getters . getStateIsDirty ;
2020-07-09 13:54:50 -07:00
if ( result ) {
const importConfirm = await this . confirmMessage ( ` When you switch workflows your current workflow changes will be lost. ` , 'Save your Changes?' , 'warning' , 'Yes, switch workflows and forget changes' ) ;
if ( importConfirm === true ) {
2020-10-25 04:47:49 -07:00
this . $store . commit ( 'setStateDirty' , false ) ;
2021-06-04 14:13:42 -07:00
if ( this . $router . currentRoute . name === 'NodeViewNew' ) {
this . $root . $emit ( 'newWorkflow' ) ;
} else {
this . $router . push ( { name : 'NodeViewNew' } ) ;
}
2020-07-09 13:54:50 -07:00
this . $showMessage ( {
title : 'Workflow created' ,
message : 'A new workflow got created!' ,
type : 'success' ,
} ) ;
}
} else {
2021-06-04 14:13:42 -07:00
if ( this . $router . currentRoute . name !== 'NodeViewNew' ) {
this . $router . push ( { name : 'NodeViewNew' } ) ;
}
2019-06-23 03:35:23 -07:00
2020-07-09 13:54:50 -07:00
this . $showMessage ( {
title : 'Workflow created' ,
message : 'A new workflow got created!' ,
type : 'success' ,
} ) ;
}
2021-01-17 05:07:07 -08:00
this . $titleReset ( ) ;
2019-06-23 03:35:23 -07:00
} else if ( key === 'credentials-open' ) {
this . credentialOpenDialogVisible = true ;
} else if ( key === 'credentials-new' ) {
this . credentialNewDialogVisible = true ;
} else if ( key === 'execution-open-workflow' ) {
if ( this . workflowExecution !== null ) {
this . openWorkflow ( this . workflowExecution . workflowId as string ) ;
}
} else if ( key === 'executions' ) {
this . executionsListDialogVisible = true ;
}
} ,
} ,
} ) ;
< / script >
< style lang = "scss" >
2020-01-09 19:53:26 -08:00
. about - icon {
padding - left : 5 px ;
}
2019-06-23 03:35:23 -07:00
# collapse - change - button {
position : absolute ;
z - index : 10 ;
top : 55 px ;
left : 25 px ;
text - align : right ;
line - height : 24 px ;
height : 20 px ;
width : 20 px ;
background - color : # fff ;
border : none ;
border - radius : 15 px ;
- webkit - transition - duration : 0.5 s ;
- moz - transition - duration : 0.5 s ;
- o - transition - duration : 0.5 s ;
transition - duration : 0.5 s ;
- webkit - transition - property : - webkit - transform ;
- moz - transition - property : - moz - transform ;
- o - transition - property : - o - transform ;
transition - property : transform ;
overflow : hidden ;
. icon {
position : relative ;
left : - 5 px ;
top : - 2 px ;
}
}
# collapse - change - button : hover {
transform : scale ( 1.1 ) ;
}
2019-09-22 12:59:45 -07:00
. el - menu - item {
a {
color : # 666 ;
2021-04-09 13:44:53 -07:00
& . primary - item {
color : $ -- color - primary ;
vertical - align : baseline ;
}
2019-09-22 12:59:45 -07:00
}
2019-06-23 03:35:23 -07:00
2019-09-22 12:59:45 -07:00
& . logo - item {
background - color : $ -- color - primary ! important ;
2021-05-29 11:31:21 -07:00
height : $ -- header - height ;
2019-09-22 12:59:45 -07:00
. icon {
position : relative ;
height : 23 px ;
left : - 10 px ;
top : - 2 px ;
}
2019-06-23 03:35:23 -07:00
}
}
2020-01-09 19:53:26 -08:00
a . logo {
text - decoration : none ;
}
. logo - text {
2019-06-23 03:35:23 -07:00
position : relative ;
top : - 3 px ;
left : 5 px ;
font - weight : bold ;
color : # fff ;
text - decoration : none ;
}
. expanded # collapse - change - button {
- webkit - transform : translateX ( 60 px ) rotate ( 180 deg ) ;
- moz - transform : translateX ( 60 px ) rotate ( 180 deg ) ;
- o - transform : translateX ( 60 px ) rotate ( 180 deg ) ;
transform : translateX ( 60 px ) rotate ( 180 deg ) ;
}
# side - menu {
position : fixed ;
height : 100 % ;
. el - menu {
height : 100 % ;
}
}
. side - menu - wrapper {
height : 100 % ;
2021-05-29 11:31:21 -07:00
width : $ -- sidebar - width ;
2019-06-23 03:35:23 -07:00
& . expanded {
2021-05-29 11:31:21 -07:00
width : $ -- sidebar - expanded - width ;
2019-06-23 03:35:23 -07:00
}
}
< / style >