feat: Execution custom data saving and filtering (#5496)
* wip: workflow execution filtering * fix: import type failing to build * fix: remove console.logs * feat: execution metadata migrations * fix(editor): Move global executions filter to its own component * fix(editor): Using the same filter component in workflow level * fix(editor): a small housekeeping * checking workflowId in filter applied * fix(editor): update filter after resolving merge conflicts * fix(editor): unify empy filter status * feat(editor): add datetime picker to filter * feat(editor): add meta fields * fix: fix button override in datepicker panel * feat(editor): add filter metadata * feat(core): add 'startedBefore' execution filter prop * feat(core): add 'tags' execution query filter * Revert "feat(core): add 'tags' execution query filter" This reverts commit a7b968081c91290b0c94df18c6a73d29950222d9. * feat(editor): add translations and tooltip and counting selected filter props * fix(editor): fix label layouts * fix(editor): update custom data docs link * fix(editor): update custom data tooltip position * fix(editor): update tooltip text * refactor: Ignore metadata if not enabled by license * fix(editor): Add paywall states to advanced execution filter * refactor: Save custom data also for worker mode * fix: Remove duplicate migration name from list * fix(editor): Reducing filter complexity and add debounce to text inputs * fix(editor): Remove unused import, add comment * fix(editor): simplify event listener * fix: Prevent error when there are running executions * test(editor): Add advanced execution filter basic unit test * test(editor): Add advanced execution filter state change unit test * fix: Small lint issue * feat: Add indices to speed up queries * feat: add customData limits * refactor: put metadata save in transaction * chore: remove unneed comment * test: add tests for execution metadata * fix(editor): Fixes after merge conflict * fix(editor): Remove unused import * wordings and ui fixes * fix(editor): type fixes * feat: add code node autocompletions for customData * fix: Prevent transaction issues and ambiguous ID in sql clauses * fix(editor): Suppress requesting current executions if metadata is used in filter (#5739) * fix(editor): Suppress requesting current executions if metadata is used in filter * fix(editor): Fix arrows for select in popover * refactor: Improve performance by correcting database indices * fix: Lint issue * test: Fix broken test * fix: Broken test * test: add call data check for saveExecutionMetadata test --------- Co-authored-by: Valya Bullions <valya@n8n.io> Co-authored-by: Alex Grozav <alex@grozav.com> Co-authored-by: Omar Ajoue <krynble@gmail.com> Co-authored-by: Romain Minaud <romain.minaud@gmail.com>
This commit is contained in:
@@ -17,64 +17,7 @@
|
||||
>
|
||||
{{ $locale.baseText('executionsList.autoRefresh') }}
|
||||
</el-checkbox>
|
||||
<n8n-popover trigger="click">
|
||||
<template #reference>
|
||||
<div :class="$style.filterButton">
|
||||
<n8n-button
|
||||
icon="filter"
|
||||
type="tertiary"
|
||||
size="medium"
|
||||
:active="statusFilterApplied"
|
||||
data-test-id="executions-filter-button"
|
||||
>
|
||||
<n8n-badge v-if="statusFilterApplied" theme="primary" class="mr-4xs">1</n8n-badge>
|
||||
{{ $locale.baseText('executionsList.filters') }}
|
||||
</n8n-button>
|
||||
</div>
|
||||
</template>
|
||||
<div :class="$style['filters-dropdown']">
|
||||
<div class="mb-s">
|
||||
<n8n-input-label
|
||||
:label="$locale.baseText('executions.ExecutionStatus')"
|
||||
:bold="false"
|
||||
size="small"
|
||||
color="text-base"
|
||||
class="mb-3xs"
|
||||
/>
|
||||
<n8n-select
|
||||
v-model="filter.status"
|
||||
size="small"
|
||||
ref="typeInput"
|
||||
:class="$style['type-input']"
|
||||
:placeholder="$locale.baseText('generic.any')"
|
||||
data-test-id="execution-status-select"
|
||||
@change="onFilterChange"
|
||||
>
|
||||
<n8n-option
|
||||
v-for="item in executionStatuses"
|
||||
:key="item.id"
|
||||
:label="item.name"
|
||||
:value="item.id"
|
||||
:data-test-id="`execution-status-${item.id}`"
|
||||
>
|
||||
</n8n-option>
|
||||
</n8n-select>
|
||||
</div>
|
||||
<div :class="[$style.filterMessage, 'mt-s']" v-if="statusFilterApplied">
|
||||
<n8n-link @click="resetFilters">
|
||||
{{ $locale.baseText('generic.reset') }}
|
||||
</n8n-link>
|
||||
</div>
|
||||
</div>
|
||||
</n8n-popover>
|
||||
</div>
|
||||
<div v-show="statusFilterApplied" class="mb-xs">
|
||||
<n8n-info-tip :bold="false">
|
||||
{{ $locale.baseText('generic.filtersApplied') }}
|
||||
<n8n-link @click="resetFilters" size="small">
|
||||
{{ $locale.baseText('generic.resetAllFilters') }}
|
||||
</n8n-link>
|
||||
</n8n-info-tip>
|
||||
<execution-filter popover-placement="left-start" @filterChanged="onFilterChanged" />
|
||||
</div>
|
||||
<div
|
||||
:class="$style.executionList"
|
||||
@@ -87,7 +30,7 @@
|
||||
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
||||
<n8n-loading :class="$style.loader" variant="p" :rows="1" />
|
||||
</div>
|
||||
<div v-if="executions.length === 0 && statusFilterApplied" :class="$style.noResultsContainer">
|
||||
<div v-if="executions.length === 0" :class="$style.noResultsContainer">
|
||||
<n8n-text color="text-base" size="medium" align="center">
|
||||
{{ $locale.baseText('executionsLandingPage.noResults') }}
|
||||
</n8n-text>
|
||||
@@ -115,20 +58,23 @@
|
||||
<script lang="ts">
|
||||
import ExecutionCard from '@/components/ExecutionsView/ExecutionCard.vue';
|
||||
import ExecutionsInfoAccordion from '@/components/ExecutionsView/ExecutionsInfoAccordion.vue';
|
||||
import ExecutionFilter from '@/components/ExecutionFilter.vue';
|
||||
import { VIEWS } from '@/constants';
|
||||
import { IExecutionsSummary } from '@/Interface';
|
||||
import type { IExecutionsSummary } from 'n8n-workflow';
|
||||
import { Route } from 'vue-router';
|
||||
import Vue from 'vue';
|
||||
import { PropType } from 'vue';
|
||||
import { mapStores } from 'pinia';
|
||||
import { useUIStore } from '@/stores/ui';
|
||||
import { useWorkflowsStore } from '@/stores/workflows';
|
||||
import { ExecutionFilterType } from '@/Interface';
|
||||
|
||||
export default Vue.extend({
|
||||
name: 'executions-sidebar',
|
||||
components: {
|
||||
ExecutionCard,
|
||||
ExecutionsInfoAccordion,
|
||||
ExecutionFilter,
|
||||
},
|
||||
props: {
|
||||
executions: {
|
||||
@@ -147,26 +93,13 @@ export default Vue.extend({
|
||||
data() {
|
||||
return {
|
||||
VIEWS,
|
||||
filter: {
|
||||
status: '',
|
||||
},
|
||||
filter: {} as ExecutionFilterType,
|
||||
autoRefresh: false,
|
||||
autoRefreshInterval: undefined as undefined | NodeJS.Timer,
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
...mapStores(useUIStore, useWorkflowsStore),
|
||||
statusFilterApplied(): boolean {
|
||||
return this.filter.status !== '';
|
||||
},
|
||||
executionStatuses(): Array<{ id: string; name: string }> {
|
||||
return [
|
||||
{ id: 'error', name: this.$locale.baseText('executionsList.error') },
|
||||
{ id: 'running', name: this.$locale.baseText('executionsList.running') },
|
||||
{ id: 'success', name: this.$locale.baseText('executionsList.success') },
|
||||
{ id: 'waiting', name: this.$locale.baseText('executionsList.waiting') },
|
||||
];
|
||||
},
|
||||
},
|
||||
watch: {
|
||||
$route(to: Route, from: Route) {
|
||||
@@ -215,8 +148,8 @@ export default Vue.extend({
|
||||
onRefresh(): void {
|
||||
this.$emit('refresh');
|
||||
},
|
||||
onFilterChange(): void {
|
||||
this.$emit('filterUpdated', this.prepareFilter());
|
||||
onFilterChanged(filter: ExecutionFilterType) {
|
||||
this.$emit('filterUpdated', filter);
|
||||
},
|
||||
reloadExecutions(): void {
|
||||
this.$emit('reloadExecutions');
|
||||
@@ -232,16 +165,6 @@ export default Vue.extend({
|
||||
this.autoRefreshInterval = setInterval(() => this.onRefresh(), 4 * 1000); // refresh data every 4 secs
|
||||
}
|
||||
},
|
||||
async resetFilters(): Promise<void> {
|
||||
this.filter.status = '';
|
||||
this.$emit('filterUpdated', this.prepareFilter());
|
||||
},
|
||||
prepareFilter(): object {
|
||||
return {
|
||||
finished: this.filter.status !== 'running',
|
||||
status: this.filter.status,
|
||||
};
|
||||
},
|
||||
checkListSize(): void {
|
||||
const sidebarContainer = this.$refs.container as HTMLElement;
|
||||
const currentExecutionCard = this.$refs[
|
||||
@@ -304,7 +227,7 @@ export default Vue.extend({
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
padding-right: var(--spacing-l);
|
||||
padding-right: var(--spacing-m);
|
||||
|
||||
button {
|
||||
display: flex;
|
||||
|
||||
Reference in New Issue
Block a user