<template>
<v-btn v-if="pullingProcess" class="loading-message" :loading="pullingProcess" text color="green">
<template v-slot:loader>
<span>Pulling requested process from server...</span>
</template>
</v-btn>
<div v-else class="editor-explorer">
<v-tabs v-model="currentTab" show-arrows="mobile" class="flex-grow-0">
<v-btn class="ml-2 mt-2" fab x-small color="primary" @click="returnToOverview()">
<v-icon> mdi-arrow-left </v-icon>
</v-btn>
<ProcessTab
v-for="(tab, index) in tabs"
:key="index"
:processDefinitionsId="tab.processDefinitionsId"
:subprocessId="tab.subprocessId"
@addTab="addTab($event.processDefinitionsId)"
@deleteTab="deleteTab(tab)"
/>
<v-btn class="d-flex align-self-center" icon center @click="showNewTabDialog = true">
<v-icon>mdi-plus</v-icon>
</v-btn>
<v-btn class="d-flex align-self-center" plain text center @click="tabs = [tabs[currentTab]]">
Clear tab bar
</v-btn>
</v-tabs>
<ProcessModal
callToActionText="Open in new tab"
:show="showNewTabDialog"
maxWidth="800px"
@cancel="showNewTabDialog = false"
@click:bpmnPreview="addTab"
></ProcessModal>
<AlertWindow :popupData="popupData" />
<EditProcessBpmn
v-if="tabs[currentTab] && tabs[currentTab].processDefinitionsId"
:processDefinitionsId="tabs[currentTab].processDefinitionsId"
:subprocessId="tabs[currentTab].subprocessId"
@addTab="addTab"
@inactivityTimeout="returnToOverview()"
></EditProcessBpmn>
</div>
</template>
<script>
import EditProcessBpmn from '@/frontend/views/ProcessBpmnEditor.vue';
import ProcessModal from '@/frontend/components/processes/editor/ProcessModal.vue';
import ProcessTab from '@/frontend/components/processes/editor/ProcessTab.vue';
import AlertWindow from '@/frontend/components/universal/Alert.vue';
import { eventHandler, processInterface } from '@/frontend/backend-api/index.js';
/**
* @module views
*/
/**
* @memberof module:views
* @module Vue:EditorExplorer
*/
export default {
name: 'edit-explorer',
components: {
ProcessTab,
ProcessModal,
EditProcessBpmn,
AlertWindow,
},
data() {
return {
/**
* Indicates the current tab
* @type {number}
*/
currentTab: 0,
/** */
tabs: [],
/** */
showNewTabDialog: false,
/** */
previousTab: 0,
/** */
popupData: {
body: '',
display: 'none',
color: 'warning',
},
processRemovedCallback: null,
pullingProcess: false,
};
},
computed: {},
watch: {
currentTab() {
if (this.tabs[this.previousTab]) {
this.signalEditingEnd(this.tabs[this.previousTab].processDefinitionsId);
}
if (this.tabs[this.currentTab]) {
this.signalEditing(this.tabs[this.currentTab].processDefinitionsId);
this.previousTab = this.currentTab;
}
const newPath = this.$router.currentRoute.path.replace(
this.$router.currentRoute.params.id,
this.tabs[this.currentTab].processDefinitionsId
);
history.replaceState({}, '', '/#' + newPath);
},
},
methods: {
async addTab(processDefinitionsId, subprocessId) {
// lookup if the tab which should be added already exists
const selectedTab = this.tabs.find(
(tab) =>
tab.processDefinitionsId === processDefinitionsId && tab.subprocessId === subprocessId
);
if (selectedTab) {
this.currentTab = this.tabs.indexOf(selectedTab);
this.showNewTabDialog = false;
return;
}
const newTab = {
processDefinitionsId,
subprocessId,
};
// to insert the new process tab infront of potential subprocesses tabs
const subprocessTabIndex = this.tabs.findIndex(
(tab) =>
tab.processDefinitionsId === processDefinitionsId && tab.subprocessId && !subprocessId
);
if (subprocessTabIndex !== -1) {
this.tabs.splice(subprocessTabIndex, 0, newTab);
} else {
this.tabs.push(newTab);
}
// switch to the added tab
this.currentTab = this.tabs.findIndex(
(tab) =>
tab.processDefinitionsId === processDefinitionsId && tab.subprocessId === subprocessId
);
this.showNewTabDialog = false;
},
/** */
deleteTab(tab) {
const index = this.tabs.indexOf(tab);
if (tab.processDefinitionsId) {
this.signalEditingEnd(tab.processDefinitionsId);
}
this.tabs.splice(index, 1);
if (this.tabs.length === 0) {
this.returnToOverview();
}
},
// Tries to pull the process that is supposed to be modeled from the backend
async pullProcess(id) {
try {
const process = await processInterface.pullProcess(id);
const bpmn = process.bpmn;
delete process.bpmn;
await this.$store.dispatch('processStore/add', { process, bpmn });
} catch (err) {
throw new Error(`Could not find process with id ${id}`);
}
},
signalEditing(definitionsId) {
this.$store.dispatch('processStore/startEditing', {
id: definitionsId,
});
this.$store.dispatch('processStore/startObservingEditing', {
id: definitionsId,
});
},
signalEditingEnd(definitionsId) {
this.$store.dispatch('processStore/stopEditing', {
id: definitionsId,
});
this.$store.dispatch('processStore/stopObservingEditing', {
id: definitionsId,
});
},
returnToOverview() {
if (this.$router.currentRoute.name === 'edit-project-bpmn') {
const projectStatusPath = this.$router.currentRoute.path.replace('bpmn', 'status');
this.$router.push({ path: projectStatusPath });
} else {
const [processesOverviewPath] = this.$router.currentRoute.path.split('/bpmn', 1);
this.$router.push({ path: processesOverviewPath });
}
},
},
mounted() {
this.processRemovedCallback = eventHandler.on('processRemoved', ({ processDefinitionsId }) => {
const deletedTab = this.tabs.find((tab) => tab.processDefinitionsId === processDefinitionsId);
if (deletedTab) {
this.deleteTab(deletedTab);
this.popupData.body = 'A process was deleted so its tab was automatically removed';
this.popupData.display = 'block';
}
});
},
async beforeMount() {
const routerProcessDefinitionsId = this.$router.currentRoute.params.id;
if (routerProcessDefinitionsId) {
// check if the process is locally available
if (!this.$store.getters['processStore/processById'](routerProcessDefinitionsId)) {
// if not try pulling it from the backend
this.pullingProcess = true;
await this.pullProcess(routerProcessDefinitionsId);
this.pullingProcess = false;
}
this.addTab(routerProcessDefinitionsId);
this.signalEditing(routerProcessDefinitionsId);
}
},
beforeRouteLeave(to, from, next) {
if (this.processRemovedCallback) {
eventHandler.off('processRemoved', this.processRemovedCallback);
}
if (this.tabs[this.currentTab]) {
this.signalEditingEnd(this.tabs[this.currentTab].processDefinitionsId);
}
next();
},
};
</script>
<style>
.editor-explorer {
display: flex;
flex: 1;
flex-direction: column;
height: 100%;
}
.loading-message {
position: absolute;
bottom: 50%;
left: 50%;
right: 50%;
}
</style>