mirror of
https://github.com/n8n-io/n8n.git
synced 2024-11-12 15:44:06 -08:00
feat(editor): Executions page (#4997)
* fix(editor): Create executions page * fix(editor): lint fix * fix(editor): Reuse execution list in both modal and page * fix(editor): fix ts issues * fix(editor): Reorganizing exec list components for easier redesign (everything is in its new place now) * fix(editor): Exec list item restyling * fix(editor): Exec list add back stripes * fix(editor): Exec list formatting dates and times * fix(editor): Exec list revert accidental searc and replace * fix(editor): Exec list translations and execution IDs * fix(editor): Exec list playing with table cell sizing * fix(editor): Exec list playing with table cell sizing * fix(editor): Exec list drop Element UI Table * fix(editor): Exec list adding sticky header and View button on row hover * fix(editor): Exec list open execution in new tab, add ellipsis menu to all rows with Delete action * fix(editor): Global exec list update translations snd fix tabindex * fix(editor): Global exec list redesign selection * fix(editor): Global exec list fix scrolling container * fix(editor): Global exec list fix running status * fix(editor): Global exec list fix waiting status
This commit is contained in:
parent
57e515dd4b
commit
819c4adb3c
File diff suppressed because it is too large
Load diff
33
packages/editor-ui/src/components/ExecutionsModal.vue
Normal file
33
packages/editor-ui/src/components/ExecutionsModal.vue
Normal file
|
@ -0,0 +1,33 @@
|
|||
<template>
|
||||
<Modal :name="EXECUTIONS_MODAL_KEY" width="80%" :eventBus="modalBus">
|
||||
<template #content>
|
||||
<ExecutionsList @closeModal="onCloseModal" />
|
||||
</template>
|
||||
</Modal>
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import ExecutionsList from '@/components/ExecutionsList.vue';
|
||||
import Modal from '@/components/Modal.vue';
|
||||
import { EXECUTIONS_MODAL_KEY } from '@/constants';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'ExecutionsModal',
|
||||
components: {
|
||||
Modal,
|
||||
ExecutionsList,
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
modalBus: new Vue(),
|
||||
EXECUTIONS_MODAL_KEY,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
onCloseModal() {
|
||||
this.modalBus.$emit('close');
|
||||
},
|
||||
},
|
||||
});
|
||||
</script>
|
|
@ -76,7 +76,7 @@ export default mixins(
|
|||
debounceHelper,
|
||||
workflowHelpers,
|
||||
).extend({
|
||||
name: 'executions-page',
|
||||
name: 'executions-list',
|
||||
components: {
|
||||
ExecutionsSidebar,
|
||||
},
|
|
@ -99,7 +99,7 @@ export default mixins(pushConnection, workflowHelpers).extend({
|
|||
syncTabsWithRoute(route: Route): void {
|
||||
if (
|
||||
route.name === VIEWS.EXECUTION_HOME ||
|
||||
route.name === VIEWS.EXECUTIONS ||
|
||||
route.name === VIEWS.WORKFLOW_EXECUTIONS ||
|
||||
route.name === VIEWS.EXECUTION_PREVIEW
|
||||
) {
|
||||
this.activeHeaderTab = MAIN_HEADER_TABS.EXECUTIONS;
|
||||
|
|
|
@ -240,7 +240,7 @@ export default mixins(workflowHelpers, titleChange).extend({
|
|||
onExecutionsTab(): boolean {
|
||||
return [
|
||||
VIEWS.EXECUTION_HOME.toString(),
|
||||
VIEWS.EXECUTIONS.toString(),
|
||||
VIEWS.WORKFLOW_EXECUTIONS.toString(),
|
||||
VIEWS.EXECUTION_PREVIEW,
|
||||
].includes(this.$route.name || '');
|
||||
},
|
||||
|
|
|
@ -93,7 +93,6 @@
|
|||
<script lang="ts">
|
||||
import { IExecutionResponse, IMenuItem, IVersion } from '../Interface';
|
||||
|
||||
import ExecutionsList from '@/components/ExecutionsList.vue';
|
||||
import GiftNotificationIcon from './GiftNotificationIcon.vue';
|
||||
import WorkflowSettings from '@/components/WorkflowSettings.vue';
|
||||
|
||||
|
@ -111,7 +110,6 @@ import {
|
|||
MODAL_CONFIRMED,
|
||||
ABOUT_MODAL_KEY,
|
||||
VERSIONS_MODAL_KEY,
|
||||
EXECUTIONS_MODAL_KEY,
|
||||
VIEWS,
|
||||
PLACEHOLDER_EMPTY_WORKFLOW_ID,
|
||||
} from '@/constants';
|
||||
|
@ -138,7 +136,6 @@ export default mixins(
|
|||
).extend({
|
||||
name: 'MainSidebar',
|
||||
components: {
|
||||
ExecutionsList,
|
||||
GiftNotificationIcon,
|
||||
WorkflowSettings,
|
||||
},
|
||||
|
@ -239,8 +236,9 @@ export default mixins(
|
|||
{
|
||||
id: 'executions',
|
||||
icon: 'tasks',
|
||||
label: this.$locale.baseText('generic.executions'),
|
||||
label: this.$locale.baseText('mainSidebar.executions'),
|
||||
position: 'top',
|
||||
activateOnRouteNames: [VIEWS.EXECUTIONS],
|
||||
},
|
||||
{
|
||||
id: 'settings',
|
||||
|
@ -390,7 +388,9 @@ export default mixins(
|
|||
break;
|
||||
}
|
||||
case 'executions': {
|
||||
this.uiStore.openModal(EXECUTIONS_MODAL_KEY);
|
||||
if (this.$router.currentRoute.name !== VIEWS.EXECUTIONS) {
|
||||
this.$router.push({ name: VIEWS.EXECUTIONS });
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 'settings': {
|
||||
|
|
|
@ -62,7 +62,7 @@
|
|||
</ModalRoot>
|
||||
|
||||
<ModalRoot :name="EXECUTIONS_MODAL_KEY">
|
||||
<ExecutionsList />
|
||||
<ExecutionsModal />
|
||||
</ModalRoot>
|
||||
|
||||
<ModalRoot :name="WORKFLOW_ACTIVE_MODAL_KEY">
|
||||
|
@ -153,7 +153,7 @@ import UpdatesPanel from './UpdatesPanel.vue';
|
|||
import ValueSurvey from './ValueSurvey.vue';
|
||||
import WorkflowSettings from './WorkflowSettings.vue';
|
||||
import DeleteUserModal from './DeleteUserModal.vue';
|
||||
import ExecutionsList from './ExecutionsList.vue';
|
||||
import ExecutionsModal from './ExecutionsModal.vue';
|
||||
import ActivationModal from './ActivationModal.vue';
|
||||
import ImportCurlModal from './ImportCurlModal.vue';
|
||||
import WorkflowShareModal from './WorkflowShareModal.ee.vue';
|
||||
|
@ -173,7 +173,7 @@ export default Vue.extend({
|
|||
DeleteUserModal,
|
||||
DuplicateWorkflowDialog,
|
||||
InviteUsersModal,
|
||||
ExecutionsList,
|
||||
ExecutionsModal,
|
||||
ModalRoot,
|
||||
OnboardingCallSignupModal,
|
||||
PersonalizationModal,
|
||||
|
|
|
@ -297,7 +297,7 @@ export enum VIEWS {
|
|||
HOMEPAGE = 'Homepage',
|
||||
COLLECTION = 'TemplatesCollectionView',
|
||||
EXECUTION = 'ExecutionById',
|
||||
EXECUTIONS = 'ExecutionList',
|
||||
EXECUTIONS = 'Executions',
|
||||
EXECUTION_PREVIEW = 'ExecutionPreview',
|
||||
EXECUTION_HOME = 'ExecutionsLandingPage',
|
||||
TEMPLATE = 'TemplatesWorkflowView',
|
||||
|
@ -319,6 +319,7 @@ export enum VIEWS {
|
|||
FAKE_DOOR = 'ComingSoon',
|
||||
COMMUNITY_NODES = 'CommunityNodes',
|
||||
WORKFLOWS = 'WorkflowsView',
|
||||
WORKFLOW_EXECUTIONS = 'WorkflowExecutions',
|
||||
USAGE = 'Usage',
|
||||
LOG_STREAMING_SETTINGS = 'LogStreamingSettingsView',
|
||||
}
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
import { showMessage } from '@/mixins/showMessage';
|
||||
import { VIEWS } from '@/constants';
|
||||
|
||||
import mixins from 'vue-typed-mixins';
|
||||
import dateformat from 'dateformat';
|
||||
|
||||
import { VIEWS } from '@/constants';
|
||||
import { showMessage } from '@/mixins/showMessage';
|
||||
|
||||
export const genericHelpers = mixins(showMessage).extend({
|
||||
data() {
|
||||
|
@ -21,17 +22,24 @@ export const genericHelpers = mixins(showMessage).extend({
|
|||
displayTimer(msPassed: number, showMs = false): string {
|
||||
if (msPassed < 60000) {
|
||||
if (!showMs) {
|
||||
return `${Math.floor(msPassed / 1000)} ${this.$locale.baseText('genericHelpers.sec')}`;
|
||||
return `${Math.floor(msPassed / 1000)}${this.$locale.baseText(
|
||||
'genericHelpers.secShort',
|
||||
)}`;
|
||||
}
|
||||
|
||||
return `${msPassed / 1000} ${this.$locale.baseText('genericHelpers.sec')}`;
|
||||
return `${msPassed / 1000}${this.$locale.baseText('genericHelpers.secShort')}`;
|
||||
}
|
||||
|
||||
const secondsPassed = Math.floor(msPassed / 1000);
|
||||
const minutesPassed = Math.floor(secondsPassed / 60);
|
||||
const secondsLeft = (secondsPassed - minutesPassed * 60).toString().padStart(2, '0');
|
||||
|
||||
return `${minutesPassed}:${secondsLeft} ${this.$locale.baseText('genericHelpers.min')}`;
|
||||
return `${minutesPassed}:${secondsLeft}${this.$locale.baseText('genericHelpers.minShort')}`;
|
||||
},
|
||||
convertToDisplayDate(epochTime: number): { date: string; time: string } {
|
||||
const formattedDate = dateformat(epochTime, 'd mmm, yyyy#HH:MM:ss');
|
||||
const [date, time] = formattedDate.split('#');
|
||||
return { date, time };
|
||||
},
|
||||
editAllowedCheck(): boolean {
|
||||
if (this.isReadOnly) {
|
||||
|
|
|
@ -430,11 +430,11 @@
|
|||
"executionsList.confirmMessage.confirmButtonText": "Yes, delete",
|
||||
"executionsList.confirmMessage.headline": "Delete Executions?",
|
||||
"executionsList.confirmMessage.message": "Are you sure that you want to delete the {numSelected} selected execution(s)?",
|
||||
"executionsList.deleteSelected": "Delete Selected",
|
||||
"executionsList.clearSelection": "Clear selection",
|
||||
"executionsList.error": "Failed",
|
||||
"executionsList.filters": "Filters",
|
||||
"executionsList.loadMore": "Load More",
|
||||
"executionsList.mode": "Mode",
|
||||
"executionsList.loadedAll": "No more executions to fetch",
|
||||
"executionsList.modes.error": "error",
|
||||
"executionsList.modes.integrated": "integrated",
|
||||
"executionsList.modes.manual": "manual",
|
||||
|
@ -449,10 +449,9 @@
|
|||
"executionsList.retryWithOriginalWorkflow": "Retry with original workflow (from node with error)",
|
||||
"executionsList.running": "Running",
|
||||
"executionsList.succeeded": "Succeeded",
|
||||
"executionsList.runningTime": "Running Time",
|
||||
"executionsList.selectStatus": "Select Status",
|
||||
"executionsList.selectWorkflow": "Select Workflow",
|
||||
"executionsList.selected": "Selected",
|
||||
"executionsList.selected": "{numSelected} execution selected:",
|
||||
"executionsList.manual": "Manual execution",
|
||||
"executionsList.showError.handleDeleteSelected.title": "Problem deleting executions",
|
||||
"executionsList.showError.loadMore.title": "Problem loading executions",
|
||||
|
@ -465,17 +464,14 @@
|
|||
"executionsList.showMessage.retrySuccessfulTrue.title": "Retry successful",
|
||||
"executionsList.showMessage.stopExecution.message": "Execution ID {activeExecutionId}",
|
||||
"executionsList.showMessage.stopExecution.title": "Execution stopped",
|
||||
"executionsList.startedAtId": "Started At / ID",
|
||||
"executionsList.startedAt": "Started At",
|
||||
"executionsList.started": "{date} at {time}",
|
||||
"executionsList.id": "Execution ID",
|
||||
"executionsList.status": "Status",
|
||||
"executionsList.statusTooltipText.theWorkflowExecutionFailed": "The workflow execution failed.",
|
||||
"executionsList.statusTooltipText.theWorkflowExecutionFailedButTheRetryWasSuccessful": "The workflow execution failed but the retry {entryRetrySuccessId} was successful.",
|
||||
"executionsList.statusTooltipText.theWorkflowExecutionIsProbablyStillRunning": "The workflow execution is probably still running but it may have crashed and n8n cannot safely tell. ",
|
||||
"executionsList.statusTooltipText.theWorkflowExecutionWasARetryOfAndFailed": "The workflow execution was a retry of {entryRetryOf} and failed.<br />New retries have to be started from the original execution.",
|
||||
"executionsList.statusTooltipText.theWorkflowExecutionWasARetryOfAndItWasSuccessful": "The workflow execution was a retry of {entryRetryOf} and it was successful.",
|
||||
"executionsList.statusTooltipText.theWorkflowExecutionWasSuccessful": "The worklow execution was successful.",
|
||||
"executionsList.statusTooltipText.theWorkflowIsCurrentlyExecuting": "The worklow is currently executing.",
|
||||
"executionsList.statusTooltipText.theWorkflowIsWaitingIndefinitely": "The workflow is waiting indefinitely for an incoming webhook call.",
|
||||
"executionsList.statusTooltipText.theWorkflowIsWaitingTill": "The worklow is waiting till {waitDateDate} {waitDateTime}.",
|
||||
"executionsList.statusText": "{status} in {time}",
|
||||
"executionsList.statusRunning": "{status} for {time}",
|
||||
"executionsList.statusWaiting": "{status} until {time}",
|
||||
"executionsList.statusUnknown": "{status}",
|
||||
"executionsList.stopExecution": "Stop Execution",
|
||||
"executionsList.success": "Success",
|
||||
"executionsList.successRetry": "Success retry",
|
||||
|
@ -483,6 +479,8 @@
|
|||
"executionsList.unsavedWorkflow": "[UNSAVED WORKFLOW]",
|
||||
"executionsList.waiting": "Waiting",
|
||||
"executionsList.workflowExecutions": "Workflow Executions",
|
||||
"executionsList.view": "View",
|
||||
"executionsList.stop": "Stop",
|
||||
"executionSidebar.executionName": "Execution {id}",
|
||||
"executionSidebar.searchPlaceholder": "Search executions...",
|
||||
"executionView.onPaste.title": "Cannot paste here",
|
||||
|
@ -534,7 +532,9 @@
|
|||
"generic.oauth2Api": "OAuth2 API",
|
||||
"genericHelpers.loading": "Loading",
|
||||
"genericHelpers.min": "min",
|
||||
"genericHelpers.minShort": "m",
|
||||
"genericHelpers.sec": "sec",
|
||||
"genericHelpers.secShort": "s",
|
||||
"genericHelpers.showMessage.message": "Executions are read-only. Make changes from the <b>Workflow</b> tab.",
|
||||
"genericHelpers.showMessage.title": "Cannot edit execution",
|
||||
"mainSidebar.aboutN8n": "About n8n",
|
||||
|
@ -566,6 +566,7 @@
|
|||
"mainSidebar.showMessage.stopExecution.title": "Execution stopped",
|
||||
"mainSidebar.templates": "Templates",
|
||||
"mainSidebar.workflows": "Workflows",
|
||||
"mainSidebar.executions": "All executions",
|
||||
"menuActions.duplicate": "Duplicate",
|
||||
"menuActions.download": "Download",
|
||||
"menuActions.importFromUrl": "Import from URL...",
|
||||
|
|
|
@ -6,7 +6,7 @@ import ForgotMyPasswordView from './views/ForgotMyPasswordView.vue';
|
|||
import MainHeader from '@/components/MainHeader/MainHeader.vue';
|
||||
import MainSidebar from '@/components/MainSidebar.vue';
|
||||
import NodeView from '@/views/NodeView.vue';
|
||||
import ExecutionsView from '@/components/ExecutionsView/ExecutionsView.vue';
|
||||
import WorkflowExecutionsList from '@/components/ExecutionsView/ExecutionsList.vue';
|
||||
import ExecutionsLandingPage from '@/components/ExecutionsView/ExecutionsLandingPage.vue';
|
||||
import ExecutionPreview from '@/components/ExecutionsView/ExecutionPreview.vue';
|
||||
import SettingsView from './views/SettingsView.vue';
|
||||
|
@ -25,6 +25,7 @@ import TemplatesCollectionView from '@/views/TemplatesCollectionView.vue';
|
|||
import TemplatesWorkflowView from '@/views/TemplatesWorkflowView.vue';
|
||||
import TemplatesSearchView from '@/views/TemplatesSearchView.vue';
|
||||
import CredentialsView from '@/views/CredentialsView.vue';
|
||||
import ExecutionsView from '@/views/ExecutionsView.vue';
|
||||
import WorkflowsView from '@/views/WorkflowsView.vue';
|
||||
import { IPermissions } from './Interface';
|
||||
import { LOGIN_STATUS, ROLE } from '@/utils';
|
||||
|
@ -199,6 +200,21 @@ const router = new Router({
|
|||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/executions',
|
||||
name: VIEWS.EXECUTIONS,
|
||||
components: {
|
||||
default: ExecutionsView,
|
||||
sidebar: MainSidebar,
|
||||
},
|
||||
meta: {
|
||||
permissions: {
|
||||
allow: {
|
||||
loginStatus: [LOGIN_STATUS.LoggedIn],
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
{
|
||||
path: '/workflow',
|
||||
redirect: '/workflow/new',
|
||||
|
@ -254,9 +270,9 @@ const router = new Router({
|
|||
},
|
||||
{
|
||||
path: '/workflow/:name/executions',
|
||||
name: VIEWS.EXECUTIONS,
|
||||
name: VIEWS.WORKFLOW_EXECUTIONS,
|
||||
components: {
|
||||
default: ExecutionsView,
|
||||
default: WorkflowExecutionsList,
|
||||
header: MainHeader,
|
||||
sidebar: MainSidebar,
|
||||
},
|
||||
|
|
15
packages/editor-ui/src/views/ExecutionsView.vue
Normal file
15
packages/editor-ui/src/views/ExecutionsView.vue
Normal file
|
@ -0,0 +1,15 @@
|
|||
<template>
|
||||
<ExecutionsList />
|
||||
</template>
|
||||
|
||||
<script lang="ts">
|
||||
import Vue from 'vue';
|
||||
import ExecutionsList from '@/components/ExecutionsList.vue';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'ExecutionsView',
|
||||
components: {
|
||||
ExecutionsList,
|
||||
},
|
||||
});
|
||||
</script>
|
Loading…
Reference in a new issue