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 -open @openWorkflow ="openWorkflow" :dialogVisible ="workflowOpenDialogVisible" @closeDialog ="closeWorkflowOpenDialog" > < / workflow -open >
< 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}" >
< div id = "collapse-change-button" class = "clickable" @click ="isCollapsed=!isCollapsed" >
< 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 >
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 >
< el -menu -item index = "workflow-save" :disabled ="!currentWorkflow" >
< 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 >
< el -menu -item index = "workflow-save-as" >
< template slot = "title" >
< font -awesome -icon icon = "copy" / >
< span slot = "title" class = "item-title" > Save As < / span >
< / template >
< / e l - m e n u - i t e m >
2019-12-20 14:58:36 -08:00
< el -menu -item index = "workflow-rename" :disabled ="!currentWorkflow" >
< template slot = "title" >
< font -awesome -icon icon = "edit" / >
< span slot = "title" class = "item-title" > Rename < / span >
< / 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 >
< el -menu -item index = "help-documentation" >
< template slot = "title" >
< a href = "https://docs.n8n.io" target = "_blank" >
< font -awesome -icon icon = "book" / >
< span slot = "title" class = "item-title" > Documentation < / span >
< / a >
< / template >
< / e l - m e n u - i t e m >
< el -menu -item index = "help-forum" >
< template slot = "title" >
< a href = "https://community.n8n.io" target = "_blank" >
< font -awesome -icon icon = "users" / >
< span slot = "title" class = "item-title" > Forum < / span >
< / a >
< / template >
< / e l - m e n u - i t e m >
< el -menu -item index = "help-examples" >
< template slot = "title" >
< a href = "https://n8n.io/workflows" target = "_blank" >
< font -awesome -icon icon = "network-wired" / >
< span slot = "title" class = "item-title" > Workflows < / span >
< / a >
< / template >
< / e l - m e n u - i t e m >
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 >
2019-06-23 03:35:23 -07:00
< / e l - m e n u >
< / div >
< / div >
< / template >
< script lang = "ts" >
import Vue from 'vue' ;
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 ,
} 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 WorkflowOpen from '@/components/WorkflowOpen.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' ;
2019-12-16 18:27:56 -08:00
import { workflowSave } from '@/components/mixins/workflowSave' ;
2019-06-23 03:35:23 -07:00
import { workflowRun } from '@/components/mixins/workflowRun' ;
import { saveAs } from 'file-saver' ;
import mixins from 'vue-typed-mixins' ;
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 ,
2019-12-16 18:27:56 -08:00
workflowSave ,
2019-06-23 03:35:23 -07:00
)
. extend ( {
name : 'MainHeader' ,
components : {
2020-01-09 19:53:26 -08:00
About ,
2019-06-23 03:35:23 -07:00
CredentialsEdit ,
CredentialsList ,
ExecutionsList ,
WorkflowOpen ,
WorkflowSettings ,
} ,
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
isCollapsed : true ,
credentialNewDialogVisible : false ,
credentialOpenDialogVisible : false ,
executionsListDialogVisible : false ,
stopExecutionInProgress : false ,
workflowOpenDialogVisible : false ,
workflowSettingsDialogVisible : false ,
} ;
} ,
computed : {
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' ) ;
} ,
} ,
methods : {
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
closeWorkflowOpenDialog ( ) {
this . workflowOpenDialogVisible = false ;
} ,
closeWorkflowSettingsDialog ( ) {
this . workflowSettingsDialogVisible = false ;
} ,
closeExecutionsListOpenDialog ( ) {
this . executionsListDialogVisible = false ;
} ,
closeCredentialOpenDialog ( ) {
this . credentialOpenDialogVisible = false ;
} ,
closeCredentialNewDialog ( ) {
this . credentialNewDialogVisible = false ;
} ,
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 } ,
} ) ;
this . workflowOpenDialogVisible = false ;
} ,
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' ) {
this . workflowOpenDialogVisible = true ;
} 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 ) { }
2019-12-20 14:58:36 -08:00
} else if ( key === 'workflow-rename' ) {
const workflowName = await this . $prompt (
'Enter new workflow name' ,
'Rename' ,
{
inputValue : this . workflowName ,
confirmButtonText : 'Rename' ,
cancelButtonText : 'Cancel' ,
2019-12-29 13:02:21 -08:00
} ,
2019-12-20 14:58:36 -08:00
)
. then ( ( data ) => {
// @ts-ignore
return data . value ;
} )
. catch ( ( ) => {
// User did cancel
return undefined ;
} ) ;
if ( workflowName === undefined || workflowName === this . workflowName ) {
return ;
}
const workflowId = this . $store . getters . workflowId ;
const updateData = {
name : workflowName ,
} ;
try {
await this . restApi ( ) . updateWorkflow ( workflowId , updateData ) ;
} catch ( error ) {
this . $showError ( error , 'Problem renaming the workflow' , 'There was a problem renaming the workflow:' ) ;
return ;
}
2020-09-09 05:28:13 -07:00
this . $store . commit ( 'setWorkflowName' , { newName : workflowName , setStateDirty : true } ) ;
2019-12-20 14:58:36 -08:00
this . $showMessage ( {
title : 'Workflow renamed' ,
message : ` The workflow got renamed to " ${ workflowName } "! ` ,
type : 'success' ,
} ) ;
2019-06-23 03:35:23 -07:00
} 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 ( ) ;
const blob = new Blob ( [ JSON . stringify ( workflowData , null , 2 ) ] , {
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 ( ) ;
} else if ( key === 'workflow-save-as' ) {
this . saveCurrentWorkflow ( true ) ;
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 ) {
this . $router . push ( { name : 'NodeViewNew' } ) ;
this . $showMessage ( {
title : 'Workflow created' ,
message : 'A new workflow got created!' ,
type : 'success' ,
} ) ;
}
} else {
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' ,
} ) ;
}
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 ;
}
} ,
} ,
async mounted ( ) {
this . $root . $on ( 'openWorkflowDialog' , async ( ) => {
this . workflowOpenDialogVisible = 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 ;
}
2019-06-23 03:35:23 -07:00
2019-09-22 12:59:45 -07:00
& . logo - item {
background - color : $ -- color - primary ! important ;
height : 65 px ;
. 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 % ;
width : 65 px ;
& . expanded {
width : 200 px ;
}
}
< / style >