mirror of
https://github.com/Abdulazizzn/n8n-enterprise-unlocked.git
synced 2025-12-16 17:46:45 +00:00
fix(editor): Feature/table reskin (no-changelog) (#13817)
This commit is contained in:
committed by
GitHub
parent
48eef63bf3
commit
09ebc3adc7
@@ -7,6 +7,7 @@ import { getValueByPath } from '../../utils';
|
||||
import N8nOption from '../N8nOption';
|
||||
import N8nPagination from '../N8nPagination';
|
||||
import N8nSelect from '../N8nSelect';
|
||||
import N8nTableBase from '../TableBase';
|
||||
|
||||
const ALL_ROWS = -1;
|
||||
|
||||
@@ -81,7 +82,7 @@ function getThStyle(column: DatatableColumn) {
|
||||
|
||||
<template>
|
||||
<div class="datatable datatableWrapper" v-bind="$attrs">
|
||||
<table>
|
||||
<N8nTableBase>
|
||||
<thead>
|
||||
<tr>
|
||||
<th
|
||||
@@ -106,7 +107,7 @@ function getThStyle(column: DatatableColumn) {
|
||||
</slot>
|
||||
</template>
|
||||
</tbody>
|
||||
</table>
|
||||
</N8nTableBase>
|
||||
|
||||
<div class="pagination">
|
||||
<N8nPagination
|
||||
@@ -142,65 +143,6 @@ function getThStyle(column: DatatableColumn) {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
:deep(table) {
|
||||
border: 1px solid var(--color-foreground-base);
|
||||
border-collapse: separate;
|
||||
overflow: hidden;
|
||||
border-spacing: 0;
|
||||
border-radius: 10px;
|
||||
text-align: left;
|
||||
width: 100%;
|
||||
background-color: var(--color-background-xlight);
|
||||
table-layout: fixed;
|
||||
|
||||
th {
|
||||
font-size: var(--font-size-2xs);
|
||||
font-weight: var(--font-weight-regular);
|
||||
padding: 10px 8px;
|
||||
}
|
||||
|
||||
td {
|
||||
font-size: var(--font-size-s);
|
||||
padding: 3px 8px;
|
||||
height: 47px;
|
||||
width: auto;
|
||||
&:first-child {
|
||||
padding-left: 16px;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
vertical-align: middle;
|
||||
border-bottom: 1px solid var(--color-foreground-base);
|
||||
white-space: nowrap;
|
||||
text-overflow: ellipsis;
|
||||
overflow: hidden;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
tbody {
|
||||
tr {
|
||||
&:hover {
|
||||
background-color: var(--color-background-light);
|
||||
}
|
||||
|
||||
&:last-child {
|
||||
th,
|
||||
td {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.datatableWrapper {
|
||||
display: block;
|
||||
width: 100%;
|
||||
|
||||
@@ -2,98 +2,102 @@
|
||||
|
||||
exports[`components > N8nDatatable > should render correctly 1`] = `
|
||||
"<div data-v-c73ff18f="" class="datatable datatableWrapper">
|
||||
<table data-v-c73ff18f="">
|
||||
<thead data-v-c73ff18f="">
|
||||
<tr data-v-c73ff18f="">
|
||||
<th data-v-c73ff18f="" class="">ID</th>
|
||||
<th data-v-c73ff18f="" class="">Name</th>
|
||||
<th data-v-c73ff18f="" class="">Age</th>
|
||||
<th data-v-c73ff18f="" class="">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-v-c73ff18f="">
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">1</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Richard Hendricks</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">29</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">2</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Bertram Gilfoyle</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">44</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">3</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Dinesh Chugtai</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">31</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">4</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Jared Dunn </span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">38</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">5</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Richard Hendricks</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">29</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">6</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Bertram Gilfoyle</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">44</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">7</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Dinesh Chugtai</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">31</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">8</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Jared Dunn </span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">38</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">9</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Richard Hendricks</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">29</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">10</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Bertram Gilfoyle</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">44</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<div data-v-c73ff18f="" class="n8nTable">
|
||||
<div class="n8nTableScroll">
|
||||
<table>
|
||||
<thead data-v-c73ff18f="">
|
||||
<tr data-v-c73ff18f="">
|
||||
<th data-v-c73ff18f="" class="">ID</th>
|
||||
<th data-v-c73ff18f="" class="">Name</th>
|
||||
<th data-v-c73ff18f="" class="">Age</th>
|
||||
<th data-v-c73ff18f="" class="">Action</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody data-v-c73ff18f="">
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">1</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Richard Hendricks</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">29</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">2</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Bertram Gilfoyle</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">44</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">3</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Dinesh Chugtai</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">31</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">4</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Jared Dunn </span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">38</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">5</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Richard Hendricks</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">29</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">6</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Bertram Gilfoyle</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">44</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">7</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Dinesh Chugtai</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">31</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">8</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Jared Dunn </span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">38</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">9</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Richard Hendricks</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">29</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
<tr data-v-c73ff18f="">
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">10</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">Bertram Gilfoyle</span></td>
|
||||
<td data-v-c73ff18f="" class=""><span data-v-c73ff18f="">44</span></td>
|
||||
<td data-v-c73ff18f="" class="">
|
||||
<n8n-button-stub data-v-c73ff18f="" block="false" element="button" label="" square="false" active="false" disabled="false" loading="false" outline="false" size="medium" text="false" type="primary" column="[object Object]"></n8n-button-stub>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
<div data-v-c73ff18f="" class="pagination">
|
||||
<div data-v-c73ff18f="" class="el-pagination is-background is-background"><button type="button" class="btn-prev is-first" disabled="" aria-label="Go to previous page" aria-disabled="true"><i class="el-icon"><svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1024 1024">
|
||||
<path fill="currentColor" d="M609.408 149.376 277.76 489.6a32 32 0 0 0 0 44.672l331.648 340.352a29.12 29.12 0 0 0 41.728 0 30.592 30.592 0 0 0 0-42.752L339.264 511.936l311.872-319.872a30.592 30.592 0 0 0 0-42.688 29.12 29.12 0 0 0-41.728 0z"></path>
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
<script setup lang="ts"></script>
|
||||
|
||||
<template>
|
||||
<div :class="$style.n8nTable">
|
||||
<div :class="$style.n8nTableScroll">
|
||||
<table>
|
||||
<slot />
|
||||
</table>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
.n8nTableScroll {
|
||||
max-height: 100%;
|
||||
overflow: auto;
|
||||
position: relative;
|
||||
}
|
||||
.n8nTable {
|
||||
height: 100%;
|
||||
border-radius: 8px;
|
||||
border: 1px solid var(--color-foreground-base);
|
||||
overflow: hidden;
|
||||
font-size: var(--font-size-s);
|
||||
|
||||
table {
|
||||
width: 100%;
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
white-space: nowrap;
|
||||
|
||||
> thead {
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 2;
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
th {
|
||||
background-color: var(--color-background-light-base);
|
||||
color: var(--color-text-base);
|
||||
font-weight: 600;
|
||||
font-size: 12px;
|
||||
padding: 0 8px;
|
||||
text-transform: capitalize;
|
||||
height: 36px;
|
||||
white-space: nowrap;
|
||||
border-bottom: 1px solid var(--color-foreground-base);
|
||||
|
||||
&:first-child {
|
||||
padding-left: 16px;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
|
||||
tbody > tr {
|
||||
&:hover {
|
||||
background-color: var(--color-background-light);
|
||||
}
|
||||
|
||||
&:last-child > td {
|
||||
border-bottom: 0;
|
||||
}
|
||||
}
|
||||
|
||||
tr {
|
||||
background-color: var(--color-background-xlight);
|
||||
}
|
||||
|
||||
td {
|
||||
color: var(--color-text-dark);
|
||||
padding: 0 8px;
|
||||
height: 48px;
|
||||
|
||||
border-bottom: 1px solid var(--color-foreground-base);
|
||||
|
||||
&:first-child {
|
||||
padding-left: 16px;
|
||||
}
|
||||
&:last-child {
|
||||
padding-right: 16px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,3 @@
|
||||
import N8nTableBase from './TableBase.vue';
|
||||
|
||||
export default N8nTableBase;
|
||||
@@ -58,3 +58,4 @@ export { default as N8nResizeObserver } from './ResizeObserver';
|
||||
export { N8nKeyboardShortcut } from './N8nKeyboardShortcut';
|
||||
export { default as N8nIconPicker } from './N8nIconPicker';
|
||||
export { default as N8nBreadcrumbs } from './N8nBreadcrumbs';
|
||||
export { default as N8nTableBase } from './TableBase';
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
<template>
|
||||
<svg width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<g>
|
||||
<path d="M7 0L7 3" stroke="currentColor" stroke-width="2" class="line one" />
|
||||
<path
|
||||
d="M11.9497 2.05031L9.82837 4.17163"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
class="line two"
|
||||
/>
|
||||
<path d="M14 7L11 7" stroke="currentColor" stroke-width="2" class="line three" />
|
||||
<path
|
||||
d="M11.9497 11.9497L9.82839 9.82839"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
class="line four"
|
||||
/>
|
||||
<path d="M7 14L7 11" stroke="currentColor" stroke-width="2" class="line five" />
|
||||
<path d="M0 7L3 7" stroke="currentColor" stroke-width="2" class="line seven" />
|
||||
|
||||
<path
|
||||
d="M2.05031 2.05031L4.17163 4.17163"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
class="line eight"
|
||||
/>
|
||||
|
||||
<path
|
||||
d="M2.05029 11.9497L4.17161 9.82839"
|
||||
stroke="currentColor"
|
||||
stroke-width="2"
|
||||
class="line six"
|
||||
/>
|
||||
</g>
|
||||
</svg>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
@keyframes fade {
|
||||
0% {
|
||||
opacity: 100;
|
||||
}
|
||||
20% {
|
||||
opacity: 0;
|
||||
}
|
||||
40% {
|
||||
opacity: 100;
|
||||
}
|
||||
}
|
||||
|
||||
.line {
|
||||
animation: fade 1.6s infinite;
|
||||
}
|
||||
|
||||
.one {
|
||||
animation-delay: 0s;
|
||||
}
|
||||
|
||||
.two {
|
||||
animation-delay: 0.2s;
|
||||
}
|
||||
|
||||
.three {
|
||||
animation-delay: 0.4s;
|
||||
}
|
||||
|
||||
.four {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.five {
|
||||
animation-delay: 0.8s;
|
||||
}
|
||||
|
||||
.six {
|
||||
animation-delay: 1s;
|
||||
}
|
||||
|
||||
.seven {
|
||||
animation-delay: 1.2s;
|
||||
}
|
||||
|
||||
.eight {
|
||||
animation-delay: 1.4s;
|
||||
}
|
||||
</style>
|
||||
@@ -1,8 +1,8 @@
|
||||
<script lang="ts" setup>
|
||||
import type { EnvironmentVariable, Rule, RuleGroup } from '@/Interface';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { computed, ref, reactive, toRaw } from 'vue';
|
||||
import { N8nFormInput, N8nButton } from '@n8n/design-system';
|
||||
import { N8nButton, N8nFormInput } from '@n8n/design-system';
|
||||
import { computed, reactive, ref, toRaw } from 'vue';
|
||||
import VariablesUsageBadge from './VariablesUsageBadge.vue';
|
||||
|
||||
const props = defineProps<{
|
||||
@@ -113,11 +113,6 @@ const handleSubmit = () => {
|
||||
</template>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
.key-cell,
|
||||
.value-cell {
|
||||
vertical-align: top;
|
||||
}
|
||||
|
||||
.value-cell {
|
||||
width: 100%;
|
||||
max-width: 50%;
|
||||
|
||||
@@ -1,22 +1,25 @@
|
||||
<script lang="ts" setup>
|
||||
import { watch, computed, ref, onMounted } from 'vue';
|
||||
import ConcurrentExecutionsHeader from '@/components/executions/ConcurrentExecutionsHeader.vue';
|
||||
import ExecutionsFilter from '@/components/executions/ExecutionsFilter.vue';
|
||||
import GlobalExecutionsListItem from '@/components/executions/global/GlobalExecutionsListItem.vue';
|
||||
import { EnterpriseEditionFeature, MODAL_CONFIRM } from '@/constants';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useMessage } from '@/composables/useMessage';
|
||||
import ProjectHeader from '@/components/Projects/ProjectHeader.vue';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useMessage } from '@/composables/useMessage';
|
||||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { EnterpriseEditionFeature, MODAL_CONFIRM } from '@/constants';
|
||||
import type { ExecutionFilterType, ExecutionSummaryWithScopes, IWorkflowDb } from '@/Interface';
|
||||
import type { ExecutionSummary } from 'n8n-workflow';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { useExecutionsStore } from '@/stores/executions.store';
|
||||
import type { PermissionsRecord } from '@/permissions';
|
||||
import { getResourcePermissions } from '@/permissions';
|
||||
import { useExecutionsStore } from '@/stores/executions.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import ProjectHeader from '@/components/Projects/ProjectHeader.vue';
|
||||
import ConcurrentExecutionsHeader from '@/components/executions/ConcurrentExecutionsHeader.vue';
|
||||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||
import { useWorkflowsStore } from '@/stores/workflows.store';
|
||||
import { N8nButton, N8nCheckbox, N8nTableBase } from '@n8n/design-system';
|
||||
import { useIntersectionObserver } from '@vueuse/core';
|
||||
import { ElSkeletonItem } from 'element-plus';
|
||||
import type { ExecutionSummary } from 'n8n-workflow';
|
||||
import { computed, ref, useTemplateRef, watch, type ComponentPublicInstance } from 'vue';
|
||||
|
||||
const props = withDefaults(
|
||||
defineProps<{
|
||||
@@ -43,7 +46,6 @@ const executionsStore = useExecutionsStore();
|
||||
const settingsStore = useSettingsStore();
|
||||
const pageRedirectionHelper = usePageRedirectionHelper();
|
||||
|
||||
const isMounted = ref(false);
|
||||
const allVisibleSelected = ref(false);
|
||||
const allExistingSelected = ref(false);
|
||||
const selectedItems = ref<Record<string, boolean>>({});
|
||||
@@ -94,10 +96,6 @@ watch(
|
||||
},
|
||||
);
|
||||
|
||||
onMounted(() => {
|
||||
isMounted.value = true;
|
||||
});
|
||||
|
||||
function handleCheckAllExistingChange() {
|
||||
allExistingSelected.value = !allExistingSelected.value;
|
||||
allVisibleSelected.value = !allExistingSelected.value;
|
||||
@@ -203,19 +201,21 @@ function getWorkflowName(workflowId: string): string | undefined {
|
||||
return workflows.value.find((data: IWorkflowDb) => data.id === workflowId)?.name;
|
||||
}
|
||||
|
||||
const loadMoreRef = useTemplateRef<ComponentPublicInstance>('loadMoreButton');
|
||||
useIntersectionObserver(loadMoreRef, ([entry]) => {
|
||||
if (!entry?.isIntersecting) return;
|
||||
void loadMore();
|
||||
});
|
||||
|
||||
async function loadMore() {
|
||||
if (executionsStore.filters.status === 'running') {
|
||||
return;
|
||||
}
|
||||
|
||||
let lastId: string | undefined;
|
||||
if (props.executions.length !== 0) {
|
||||
const lastItem = props.executions.slice(-1)[0];
|
||||
lastId = lastItem.id;
|
||||
}
|
||||
const lastItem = props.executions.at(-1);
|
||||
|
||||
try {
|
||||
await executionsStore.fetchExecutions(executionsStore.executionsFilters, lastId);
|
||||
await executionsStore.fetchExecutions(executionsStore.executionsFilters, lastItem?.id);
|
||||
} catch (error) {
|
||||
toast.showError(error, i18n.baseText('executionsList.showError.loadMore.title'));
|
||||
}
|
||||
@@ -335,121 +335,131 @@ const goToUpgrade = () => {
|
||||
<template>
|
||||
<div :class="$style.execListWrapper">
|
||||
<ProjectHeader />
|
||||
<div :class="$style.execList">
|
||||
<div :class="$style.execListHeader">
|
||||
<div :class="$style.execListHeaderControls">
|
||||
<ConcurrentExecutionsHeader
|
||||
v-if="settingsStore.isConcurrencyEnabled"
|
||||
class="mr-xl"
|
||||
:running-executions-count="runningExecutionsCount"
|
||||
:concurrency-cap="settingsStore.concurrency"
|
||||
:is-cloud-deployment="settingsStore.isCloudDeployment"
|
||||
@go-to-upgrade="goToUpgrade"
|
||||
/>
|
||||
<N8nLoading v-if="!isMounted" :class="$style.filterLoader" variant="custom" />
|
||||
<ElCheckbox
|
||||
v-else
|
||||
v-model="executionsStore.autoRefresh"
|
||||
class="mr-xl"
|
||||
data-test-id="execution-auto-refresh-checkbox"
|
||||
@update:model-value="onAutoRefreshToggle($event)"
|
||||
>
|
||||
{{ i18n.baseText('executionsList.autoRefresh') }}
|
||||
</ElCheckbox>
|
||||
<ExecutionsFilter
|
||||
v-show="isMounted"
|
||||
:workflows="workflows"
|
||||
class="execFilter"
|
||||
@filter-changed="onFilterChanged"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<ElCheckbox
|
||||
v-if="allVisibleSelected && total > 0"
|
||||
:class="$style.selectAll"
|
||||
:label="
|
||||
i18n.baseText('executionsList.selectAll', {
|
||||
adjustToNumber: total,
|
||||
interpolate: { executionNum: `${total}` },
|
||||
})
|
||||
"
|
||||
:model-value="allExistingSelected"
|
||||
data-test-id="select-all-executions-checkbox"
|
||||
@update:model-value="handleCheckAllExistingChange"
|
||||
<div :class="$style.execListHeaderControls">
|
||||
<ExecutionsFilter
|
||||
:workflows="workflows"
|
||||
class="execFilter"
|
||||
@filter-changed="onFilterChanged"
|
||||
/>
|
||||
|
||||
<div v-if="!isMounted">
|
||||
<N8nLoading :class="$style.tableLoader" variant="custom" />
|
||||
<N8nLoading :class="$style.tableLoader" variant="custom" />
|
||||
<N8nLoading :class="$style.tableLoader" variant="custom" />
|
||||
</div>
|
||||
<table v-else :class="$style.execTable">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>
|
||||
<el-checkbox
|
||||
:model-value="allVisibleSelected"
|
||||
:disabled="total < 1"
|
||||
label=""
|
||||
data-test-id="select-visible-executions-checkbox"
|
||||
@update:model-value="handleCheckAllVisibleChange"
|
||||
/>
|
||||
</th>
|
||||
<th>{{ i18n.baseText('executionsList.name') }}</th>
|
||||
<th>{{ i18n.baseText('executionsList.startedAt') }}</th>
|
||||
<th>{{ i18n.baseText('executionsList.status') }}</th>
|
||||
<th>{{ i18n.baseText('executionsList.id') }}</th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
<th></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<TransitionGroup tag="tbody" name="executions-list">
|
||||
<GlobalExecutionsListItem
|
||||
v-for="execution in executions"
|
||||
:key="execution.id"
|
||||
:execution="execution"
|
||||
:workflow-name="getExecutionWorkflowName(execution)"
|
||||
:workflow-permissions="getExecutionWorkflowPermissions(execution)"
|
||||
:selected="selectedItems[execution.id] || allExistingSelected"
|
||||
:concurrency-cap="settingsStore.concurrency"
|
||||
:is-cloud-deployment="settingsStore.isCloudDeployment"
|
||||
data-test-id="global-execution-list-item"
|
||||
@stop="stopExecution"
|
||||
@delete="deleteExecution"
|
||||
@select="toggleSelectExecution"
|
||||
@retry-saved="retrySavedExecution"
|
||||
@retry-original="retryOriginalExecution"
|
||||
@go-to-upgrade="goToUpgrade"
|
||||
/>
|
||||
</TransitionGroup>
|
||||
</table>
|
||||
|
||||
<div
|
||||
v-if="!executions.length && isMounted && !executionsStore.loading"
|
||||
:class="$style.loadedAll"
|
||||
data-test-id="execution-list-empty"
|
||||
>
|
||||
{{ i18n.baseText('executionsList.empty') }}
|
||||
</div>
|
||||
<div v-else-if="total > executions.length || estimated" :class="$style.loadMore">
|
||||
<N8nButton
|
||||
icon="sync"
|
||||
:title="i18n.baseText('executionsList.loadMore')"
|
||||
:label="i18n.baseText('executionsList.loadMore')"
|
||||
:loading="executionsStore.loading"
|
||||
data-test-id="load-more-button"
|
||||
@click="loadMore()"
|
||||
<div style="margin-left: auto">
|
||||
<ConcurrentExecutionsHeader
|
||||
v-if="settingsStore.isConcurrencyEnabled"
|
||||
:running-executions-count="runningExecutionsCount"
|
||||
:concurrency-cap="settingsStore.concurrency"
|
||||
:is-cloud-deployment="settingsStore.isCloudDeployment"
|
||||
@go-to-upgrade="goToUpgrade"
|
||||
/>
|
||||
<ElCheckbox
|
||||
v-else
|
||||
v-model="executionsStore.autoRefresh"
|
||||
data-test-id="execution-auto-refresh-checkbox"
|
||||
@update:model-value="onAutoRefreshToggle($event)"
|
||||
>
|
||||
{{ i18n.baseText('executionsList.autoRefresh') }}
|
||||
</ElCheckbox>
|
||||
</div>
|
||||
<div
|
||||
v-else-if="isMounted && !executionsStore.loading"
|
||||
:class="$style.loadedAll"
|
||||
data-test-id="execution-all-loaded"
|
||||
>
|
||||
{{ i18n.baseText('executionsList.loadedAll') }}
|
||||
</div>
|
||||
<div :class="$style.execList">
|
||||
<div :class="$style.execTable">
|
||||
<N8nTableBase>
|
||||
<thead>
|
||||
<tr v-if="allVisibleSelected && total > 0">
|
||||
<th style="width: 50px">
|
||||
<N8nCheckbox
|
||||
:model-value="allExistingSelected"
|
||||
data-test-id="select-all-executions-checkbox"
|
||||
@update:model-value="handleCheckAllExistingChange"
|
||||
/>
|
||||
</th>
|
||||
<th colspan="8">
|
||||
{{
|
||||
i18n.baseText('executionsList.selectAll', {
|
||||
adjustToNumber: total,
|
||||
interpolate: { executionNum: `${total}` },
|
||||
})
|
||||
}}
|
||||
</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<th style="width: 50px">
|
||||
<N8nCheckbox
|
||||
:model-value="allVisibleSelected"
|
||||
:disabled="total < 1"
|
||||
data-test-id="select-visible-executions-checkbox"
|
||||
@update:model-value="handleCheckAllVisibleChange"
|
||||
/>
|
||||
</th>
|
||||
<th>
|
||||
{{ i18n.baseText('generic.workflow') }}
|
||||
</th>
|
||||
<th>{{ i18n.baseText('executionsList.status') }}</th>
|
||||
<th>
|
||||
{{ i18n.baseText('executionsList.startedAt') }}
|
||||
</th>
|
||||
<th>
|
||||
{{ i18n.baseText('executionsList.runTime') }}
|
||||
</th>
|
||||
|
||||
<th>{{ i18n.baseText('executionsList.id') }}</th>
|
||||
|
||||
<th>
|
||||
{{ i18n.baseText('executionsList.trigger') }}
|
||||
</th>
|
||||
<th style="width: 69px"></th>
|
||||
<th style="width: 50px"></th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<GlobalExecutionsListItem
|
||||
v-for="execution in executions"
|
||||
:key="execution.id"
|
||||
:execution="execution"
|
||||
:workflow-name="getExecutionWorkflowName(execution)"
|
||||
:workflow-permissions="getExecutionWorkflowPermissions(execution)"
|
||||
:selected="selectedItems[execution.id] || allExistingSelected"
|
||||
:concurrency-cap="settingsStore.concurrency"
|
||||
:is-cloud-deployment="settingsStore.isCloudDeployment"
|
||||
data-test-id="global-execution-list-item"
|
||||
@stop="stopExecution"
|
||||
@delete="deleteExecution"
|
||||
@select="toggleSelectExecution"
|
||||
@retry-saved="retrySavedExecution"
|
||||
@retry-original="retryOriginalExecution"
|
||||
@go-to-upgrade="goToUpgrade"
|
||||
/>
|
||||
<template v-if="executionsStore.loading && !executions.length">
|
||||
<tr v-for="item in executionsStore.itemsPerPage" :key="item">
|
||||
<td v-for="col in 9" :key="col">
|
||||
<ElSkeletonItem />
|
||||
</td>
|
||||
</tr>
|
||||
</template>
|
||||
<tr>
|
||||
<td colspan="9" style="text-align: center">
|
||||
<template v-if="!executions.length">
|
||||
<span data-test-id="execution-list-empty">
|
||||
{{ i18n.baseText('executionsList.empty') }}
|
||||
</span>
|
||||
</template>
|
||||
<template v-else-if="total > executions.length || estimated">
|
||||
<N8nButton
|
||||
ref="loadMoreButton"
|
||||
icon="sync"
|
||||
:title="i18n.baseText('executionsList.loadMore')"
|
||||
:label="i18n.baseText('executionsList.loadMore')"
|
||||
:loading="executionsStore.loading"
|
||||
data-test-id="load-more-button"
|
||||
@click="loadMore()"
|
||||
/>
|
||||
</template>
|
||||
<template v-else>
|
||||
{{ i18n.baseText('executionsList.loadedAll') }}
|
||||
</template>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</N8nTableBase>
|
||||
</div>
|
||||
</div>
|
||||
<div
|
||||
@@ -483,36 +493,25 @@ const goToUpgrade = () => {
|
||||
|
||||
<style module lang="scss">
|
||||
.execListWrapper {
|
||||
display: grid;
|
||||
grid-template-rows: auto auto 1fr 0;
|
||||
position: relative;
|
||||
height: 100%;
|
||||
padding: var(--spacing-l) var(--spacing-2xl);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
overflow: hidden;
|
||||
width: 100%;
|
||||
padding: var(--spacing-l) var(--spacing-2xl) 0;
|
||||
max-width: var(--content-container-width);
|
||||
|
||||
@include mixins.breakpoint('xs-only') {
|
||||
padding: var(--spacing-xs) var(--spacing-xs) 0;
|
||||
}
|
||||
}
|
||||
|
||||
.execList {
|
||||
position: relative;
|
||||
height: 100%;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.execListHeader {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: var(--spacing-s);
|
||||
flex-shrink: 1; /* Allows shrinking when needed */
|
||||
max-height: 100%; /* Prevents overflowing the parent */
|
||||
overflow: auto; /* Scroll only when needed */
|
||||
}
|
||||
|
||||
.execListHeaderControls {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
justify-content: flex-start;
|
||||
margin-bottom: var(--spacing-s);
|
||||
}
|
||||
|
||||
.selectionOptions {
|
||||
@@ -535,87 +534,8 @@ const goToUpgrade = () => {
|
||||
}
|
||||
|
||||
.execTable {
|
||||
/*
|
||||
Table height needs to be set to 0 in order to use height 100% for elements in table cells
|
||||
*/
|
||||
height: 0;
|
||||
width: 100%;
|
||||
text-align: left;
|
||||
font-size: var(--font-size-s);
|
||||
|
||||
thead th {
|
||||
position: sticky;
|
||||
top: calc(var(--spacing-3xl) * -1);
|
||||
z-index: 2;
|
||||
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) 0;
|
||||
background: var(--color-table-header-background);
|
||||
|
||||
&:first-child {
|
||||
padding-left: var(--spacing-s);
|
||||
}
|
||||
}
|
||||
|
||||
th,
|
||||
td {
|
||||
height: 100%;
|
||||
padding: var(--spacing-s) var(--spacing-s) var(--spacing-s) 0;
|
||||
|
||||
&:not(:first-child, :nth-last-child(-n + 3)) {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
&:nth-last-child(-n + 2) {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
@media (min-width: $breakpoint-sm) {
|
||||
&:not(:nth-child(2)) {
|
||||
&,
|
||||
div,
|
||||
span {
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.loadMore {
|
||||
margin: var(--spacing-m) 0;
|
||||
width: 100%;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.loadedAll {
|
||||
text-align: center;
|
||||
font-size: var(--font-size-s);
|
||||
color: var(--color-text-light);
|
||||
margin: var(--spacing-l) 0;
|
||||
}
|
||||
|
||||
.actions.deleteOnly {
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.retryAction + .deleteAction {
|
||||
border-top: 1px solid var(--color-foreground-light);
|
||||
}
|
||||
|
||||
.selectAll {
|
||||
display: inline-block;
|
||||
margin: 0 0 var(--spacing-s) var(--spacing-s);
|
||||
color: var(--execution-select-all-text);
|
||||
}
|
||||
|
||||
.filterLoader {
|
||||
width: 220px;
|
||||
height: 32px;
|
||||
}
|
||||
|
||||
.tableLoader {
|
||||
width: 100%;
|
||||
height: 48px;
|
||||
margin-bottom: var(--spacing-2xs);
|
||||
height: 100%;
|
||||
flex: 0 1 auto;
|
||||
}
|
||||
</style>
|
||||
|
||||
|
||||
@@ -98,21 +98,6 @@ describe('GlobalExecutionsListItem', () => {
|
||||
expect(emitted().delete).toBeTruthy();
|
||||
});
|
||||
|
||||
it('should open a new window on execution click', async () => {
|
||||
global.window.open = vi.fn();
|
||||
|
||||
const { getByText } = renderComponent({
|
||||
props: {
|
||||
execution: { status: 'success', id: 123, workflowName: 'TestWorkflow' },
|
||||
workflowPermissions: {},
|
||||
},
|
||||
});
|
||||
|
||||
await fireEvent.click(getByText('TestWorkflow'));
|
||||
expect(window.open).toHaveBeenCalledWith('mockedRoute', '_blank');
|
||||
expect(globalExecutionsListItemQueuedTooltipRenderSpy).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should show formatted start date', () => {
|
||||
const testDate = '2022-01-01T12:00:00Z';
|
||||
const { getByText } = renderComponent({
|
||||
@@ -123,7 +108,7 @@ describe('GlobalExecutionsListItem', () => {
|
||||
});
|
||||
|
||||
expect(
|
||||
getByText(`1 Jan, 2022 at ${DateTime.fromJSDate(new Date(testDate)).toFormat('HH')}:00:00`),
|
||||
getByText(`1 Jan, 2022, ${DateTime.fromJSDate(new Date(testDate)).toFormat('HH')}:00:00`),
|
||||
).toBeInTheDocument();
|
||||
});
|
||||
|
||||
|
||||
@@ -1,14 +1,23 @@
|
||||
<script lang="ts" setup>
|
||||
import { ref, computed, useCssModule } from 'vue';
|
||||
import type { ExecutionSummary } from 'n8n-workflow';
|
||||
import { WAIT_INDEFINITELY } from 'n8n-workflow';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { convertToDisplayDate } from '@/utils/formatters/dateFormatter';
|
||||
import { i18n as locale } from '@/plugins/i18n';
|
||||
import AnimatedSpinner from '@/components/AnimatedSpinner.vue';
|
||||
import ExecutionsTime from '@/components/executions/ExecutionsTime.vue';
|
||||
import { useExecutionHelpers } from '@/composables/useExecutionHelpers';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { VIEWS } from '@/constants';
|
||||
import type { PermissionsRecord } from '@/permissions';
|
||||
import GlobalExecutionsListItemQueuedTooltip from '@/components/executions/global/GlobalExecutionsListItemQueuedTooltip.vue';
|
||||
import { convertToDisplayDate } from '@/utils/formatters/dateFormatter';
|
||||
import {
|
||||
N8nButton,
|
||||
N8nCheckbox,
|
||||
N8nIcon,
|
||||
N8nIconButton,
|
||||
N8nText,
|
||||
N8nTooltip,
|
||||
} from '@n8n/design-system';
|
||||
import type { IconColor } from '@n8n/design-system/types/icon';
|
||||
import type { ExecutionStatus, ExecutionSummary } from 'n8n-workflow';
|
||||
import { WAIT_INDEFINITELY } from 'n8n-workflow';
|
||||
import { computed, ref, useCssModule } from 'vue';
|
||||
|
||||
type Command = 'retrySaved' | 'retryOriginal' | 'delete';
|
||||
|
||||
@@ -37,18 +46,12 @@ const props = withDefaults(
|
||||
);
|
||||
|
||||
const style = useCssModule();
|
||||
const i18n = useI18n();
|
||||
const locale = useI18n();
|
||||
const executionHelpers = useExecutionHelpers();
|
||||
|
||||
const isStopping = ref(false);
|
||||
|
||||
const isRunning = computed(() => {
|
||||
return props.execution.status === 'running';
|
||||
});
|
||||
|
||||
const isQueued = computed(() => {
|
||||
return props.execution.status === 'new';
|
||||
});
|
||||
const isRunning = computed(() => props.execution.status === 'running');
|
||||
|
||||
const isWaitTillIndefinite = computed(() => {
|
||||
if (!props.execution.waitTill) {
|
||||
@@ -60,17 +63,63 @@ const isWaitTillIndefinite = computed(() => {
|
||||
|
||||
const isRetriable = computed(() => executionHelpers.isExecutionRetriable(props.execution));
|
||||
|
||||
const EXECUTION_STATUS = {
|
||||
CRASHED: 'crashed',
|
||||
ERROR: 'error',
|
||||
WAITING: 'waiting',
|
||||
SUCCESS: 'success',
|
||||
NEW: 'new',
|
||||
RUNNING: 'running',
|
||||
UNKNOWN: 'unknown',
|
||||
CANCELED: 'canceled',
|
||||
} as const;
|
||||
|
||||
const executionIconStatusDictionary: Record<ExecutionStatus, { icon: string; color: IconColor }> = {
|
||||
[EXECUTION_STATUS.CRASHED]: {
|
||||
icon: 'status-error',
|
||||
color: 'danger',
|
||||
},
|
||||
[EXECUTION_STATUS.ERROR]: {
|
||||
icon: 'status-error',
|
||||
color: 'danger',
|
||||
},
|
||||
[EXECUTION_STATUS.WAITING]: {
|
||||
icon: 'status-waiting',
|
||||
color: 'secondary',
|
||||
},
|
||||
[EXECUTION_STATUS.SUCCESS]: {
|
||||
icon: 'status-completed',
|
||||
color: 'success',
|
||||
},
|
||||
[EXECUTION_STATUS.NEW]: {
|
||||
icon: 'status-new',
|
||||
color: 'foreground-xdark',
|
||||
},
|
||||
[EXECUTION_STATUS.RUNNING]: {
|
||||
icon: 'spinner',
|
||||
color: 'secondary',
|
||||
},
|
||||
[EXECUTION_STATUS.UNKNOWN]: {
|
||||
icon: 'status-unknown',
|
||||
color: 'foreground-xdark',
|
||||
},
|
||||
[EXECUTION_STATUS.CANCELED]: {
|
||||
icon: 'status-canceled',
|
||||
color: 'foreground-xdark',
|
||||
},
|
||||
};
|
||||
|
||||
const errorStatuses: ExecutionStatus[] = [EXECUTION_STATUS.ERROR, EXECUTION_STATUS.CRASHED];
|
||||
const classes = computed(() => {
|
||||
return {
|
||||
[style.executionListItem]: true,
|
||||
[style[props.execution.status]]: true,
|
||||
[style.dangerBg]: errorStatuses.includes(props.execution.status),
|
||||
};
|
||||
});
|
||||
|
||||
const formattedStartedAtDate = computed(() => {
|
||||
return props.execution.startedAt
|
||||
? formatDate(props.execution.startedAt)
|
||||
: i18n.baseText('executionsList.startingSoon');
|
||||
: locale.baseText('executionsList.startingSoon');
|
||||
});
|
||||
|
||||
const formattedWaitTillDate = computed(() => {
|
||||
@@ -79,7 +128,7 @@ const formattedWaitTillDate = computed(() => {
|
||||
|
||||
const formattedStoppedAtDate = computed(() => {
|
||||
return props.execution.stoppedAt
|
||||
? i18n.displayTimer(
|
||||
? locale.displayTimer(
|
||||
new Date(props.execution.stoppedAt).getTime() -
|
||||
new Date(props.execution.startedAt).getTime(),
|
||||
true,
|
||||
@@ -87,48 +136,18 @@ const formattedStoppedAtDate = computed(() => {
|
||||
: '';
|
||||
});
|
||||
|
||||
const statusText = computed(() => {
|
||||
switch (props.execution.status) {
|
||||
case 'waiting':
|
||||
return i18n.baseText('executionsList.waiting');
|
||||
case 'canceled':
|
||||
return i18n.baseText('executionsList.canceled');
|
||||
case 'crashed':
|
||||
return i18n.baseText('executionsList.error');
|
||||
case 'new':
|
||||
return i18n.baseText('executionsList.new');
|
||||
case 'running':
|
||||
return i18n.baseText('executionsList.running');
|
||||
case 'success':
|
||||
return i18n.baseText('executionsList.succeeded');
|
||||
case 'error':
|
||||
return i18n.baseText('executionsList.error');
|
||||
default:
|
||||
return i18n.baseText('executionsList.unknown');
|
||||
function getStatusLabel(status: ExecutionStatus) {
|
||||
if (status === EXECUTION_STATUS.CRASHED) {
|
||||
return locale.baseText('executionsList.error');
|
||||
}
|
||||
});
|
||||
return locale.baseText(`executionsList.${status}`);
|
||||
}
|
||||
|
||||
const statusTextTranslationPath = computed(() => {
|
||||
switch (props.execution.status) {
|
||||
case 'waiting':
|
||||
return 'executionsList.statusWaiting';
|
||||
case 'canceled':
|
||||
return 'executionsList.statusCanceled';
|
||||
case 'crashed':
|
||||
case 'error':
|
||||
case 'success':
|
||||
if (!props.execution.stoppedAt) {
|
||||
return 'executionsList.statusTextWithoutTime';
|
||||
} else {
|
||||
return 'executionsList.statusText';
|
||||
}
|
||||
case 'new':
|
||||
return 'executionsList.statusTextWithoutTime';
|
||||
case 'running':
|
||||
return 'executionsList.statusRunning';
|
||||
default:
|
||||
return 'executionsList.statusUnknown';
|
||||
}
|
||||
const statusRender = computed(() => {
|
||||
return {
|
||||
...executionIconStatusDictionary[props.execution.status],
|
||||
label: getStatusLabel(props.execution.status),
|
||||
};
|
||||
});
|
||||
|
||||
function formatDate(fullDate: Date | string | number) {
|
||||
@@ -136,10 +155,6 @@ function formatDate(fullDate: Date | string | number) {
|
||||
return locale.baseText('executionsList.started', { interpolate: { time, date } });
|
||||
}
|
||||
|
||||
function displayExecution() {
|
||||
executionHelpers.openExecutionInNewTab(props.execution.id, props.execution.workflowId);
|
||||
}
|
||||
|
||||
function onStopExecution() {
|
||||
isStopping.value = true;
|
||||
emit('stop', props.execution);
|
||||
@@ -157,108 +172,96 @@ async function handleActionItemClick(commandData: Command) {
|
||||
<template>
|
||||
<tr :class="classes">
|
||||
<td>
|
||||
<ElCheckbox
|
||||
v-if="!!execution.stoppedAt && execution.id"
|
||||
<N8nCheckbox
|
||||
:model-value="selected"
|
||||
label=""
|
||||
data-test-id="select-execution-checkbox"
|
||||
:disabled="!Boolean(execution.id && execution.stoppedAt)"
|
||||
@update:model-value="onSelect"
|
||||
/>
|
||||
</td>
|
||||
<td>
|
||||
<span :class="$style.link" @click.stop="displayExecution">
|
||||
{{ execution.workflowName || workflowName }}
|
||||
</span>
|
||||
<RouterLink
|
||||
:to="{
|
||||
name: VIEWS.EXECUTION_PREVIEW,
|
||||
params: { name: execution.workflowId, executionId: execution.id },
|
||||
}"
|
||||
target="_blank"
|
||||
>
|
||||
<N8nText color="text-dark">
|
||||
{{ execution.workflowName || workflowName }}
|
||||
</N8nText>
|
||||
</RouterLink>
|
||||
</td>
|
||||
<td data-test-id="execution-status">
|
||||
<GlobalExecutionsListItemQueuedTooltip
|
||||
v-if="isWaitTillIndefinite || execution.status === EXECUTION_STATUS.NEW"
|
||||
:status="props.execution.status"
|
||||
:concurrency-cap="props.concurrencyCap"
|
||||
:is-cloud-deployment="props.isCloudDeployment"
|
||||
@go-to-upgrade="emit('goToUpgrade')"
|
||||
>
|
||||
<div>
|
||||
<N8nIcon :icon="statusRender.icon" :color="statusRender.color" class="mr-2xs" />
|
||||
{{ statusRender.label }}
|
||||
</div>
|
||||
</GlobalExecutionsListItemQueuedTooltip>
|
||||
<N8nTooltip
|
||||
v-else
|
||||
:disabled="execution.status !== EXECUTION_STATUS.WAITING"
|
||||
:content="
|
||||
locale.baseText('executionsList.statusWaiting', {
|
||||
interpolate: { status: execution.status, time: formattedWaitTillDate },
|
||||
})
|
||||
"
|
||||
>
|
||||
<div>
|
||||
<N8nText
|
||||
v-if="execution.status === EXECUTION_STATUS.RUNNING"
|
||||
color="secondary"
|
||||
class="mr-2xs"
|
||||
>
|
||||
<AnimatedSpinner />
|
||||
</N8nText>
|
||||
<N8nIcon v-else :icon="statusRender.icon" :color="statusRender.color" class="mr-2xs" />
|
||||
{{ statusRender.label }}
|
||||
</div>
|
||||
</N8nTooltip>
|
||||
</td>
|
||||
<td>
|
||||
<span>{{ formattedStartedAtDate }}</span>
|
||||
{{ formattedStartedAtDate }}
|
||||
</td>
|
||||
<td>
|
||||
<div :class="$style.statusColumn">
|
||||
<span v-if="isRunning" :class="$style.spinner">
|
||||
<FontAwesomeIcon icon="spinner" spin />
|
||||
</span>
|
||||
<i18n-t
|
||||
v-if="!isWaitTillIndefinite && !isQueued"
|
||||
data-test-id="execution-status"
|
||||
tag="span"
|
||||
:keypath="statusTextTranslationPath"
|
||||
>
|
||||
<template #status>
|
||||
<span :class="$style.status">{{ statusText }}</span>
|
||||
</template>
|
||||
<template #time>
|
||||
<span v-if="execution.waitTill">{{ formattedWaitTillDate }}</span>
|
||||
<span v-else-if="!!execution.stoppedAt">
|
||||
{{ formattedStoppedAtDate }}
|
||||
</span>
|
||||
<ExecutionsTime
|
||||
v-else-if="execution.status !== 'new'"
|
||||
:start-time="execution.startedAt"
|
||||
/>
|
||||
</template>
|
||||
</i18n-t>
|
||||
<GlobalExecutionsListItemQueuedTooltip
|
||||
v-else
|
||||
:status="props.execution.status"
|
||||
:concurrency-cap="props.concurrencyCap"
|
||||
:is-cloud-deployment="props.isCloudDeployment"
|
||||
@go-to-upgrade="emit('goToUpgrade')"
|
||||
>
|
||||
<span :class="$style.status">{{ statusText }}</span>
|
||||
</GlobalExecutionsListItemQueuedTooltip>
|
||||
</div>
|
||||
<template v-if="formattedStoppedAtDate">
|
||||
{{ formattedStoppedAtDate }}
|
||||
</template>
|
||||
<ExecutionsTime v-else :start-time="execution.startedAt" />
|
||||
</td>
|
||||
<td>
|
||||
<span v-if="execution.id">#{{ execution.id }}</span>
|
||||
<span v-if="execution.id">{{ execution.id }}</span>
|
||||
<span v-if="execution.retryOf">
|
||||
<br />
|
||||
<small> ({{ i18n.baseText('executionsList.retryOf') }} #{{ execution.retryOf }}) </small>
|
||||
<small> ({{ locale.baseText('executionsList.retryOf') }} {{ execution.retryOf }}) </small>
|
||||
</span>
|
||||
<span v-else-if="execution.retrySuccessId">
|
||||
<br />
|
||||
<small>
|
||||
({{ i18n.baseText('executionsList.successRetry') }} #{{ execution.retrySuccessId }})
|
||||
({{ locale.baseText('executionsList.successRetry') }} {{ execution.retrySuccessId }})
|
||||
</small>
|
||||
</span>
|
||||
</td>
|
||||
<td>
|
||||
<N8nTooltip v-if="execution.mode === 'manual'" placement="top">
|
||||
<template #content>
|
||||
<span>{{ i18n.baseText('executionsList.test') }}</span>
|
||||
</template>
|
||||
<FontAwesomeIcon icon="flask" />
|
||||
</N8nTooltip>
|
||||
<N8nTooltip v-if="execution.mode === 'evaluation'" placement="top">
|
||||
<template #content>
|
||||
<span>{{ i18n.baseText('executionsList.evaluation') }}</span>
|
||||
</template>
|
||||
<FontAwesomeIcon icon="tasks" />
|
||||
</N8nTooltip>
|
||||
<span :class="$style.capitalize">{{ execution.mode }}</span>
|
||||
</td>
|
||||
<td>
|
||||
<div :class="$style.buttonCell">
|
||||
<N8nButton
|
||||
v-if="!!execution.stoppedAt && execution.id"
|
||||
size="small"
|
||||
outline
|
||||
:label="i18n.baseText('executionsList.view')"
|
||||
@click.stop="displayExecution"
|
||||
/>
|
||||
</div>
|
||||
</td>
|
||||
<td>
|
||||
<div :class="$style.buttonCell">
|
||||
<N8nButton
|
||||
v-if="!execution.stoppedAt || execution.waitTill"
|
||||
data-test-id="stop-execution-button"
|
||||
size="small"
|
||||
outline
|
||||
:label="i18n.baseText('executionsList.stop')"
|
||||
:loading="isStopping"
|
||||
@click.stop="onStopExecution"
|
||||
/>
|
||||
</div>
|
||||
<N8nButton
|
||||
v-if="!execution.stoppedAt || execution.waitTill"
|
||||
data-test-id="stop-execution-button"
|
||||
:loading="isStopping"
|
||||
:disabled="isStopping"
|
||||
@click.stop="onStopExecution"
|
||||
>
|
||||
{{ locale.baseText('executionsList.stop') }}
|
||||
</N8nButton>
|
||||
</td>
|
||||
<td>
|
||||
<ElDropdown v-if="!isRunning" trigger="click" @command="handleActionItemClick">
|
||||
@@ -277,7 +280,7 @@ async function handleActionItemClick(commandData: Command) {
|
||||
command="retrySaved"
|
||||
:disabled="!workflowPermissions.execute"
|
||||
>
|
||||
{{ i18n.baseText('executionsList.retryWithCurrentlySavedWorkflow') }}
|
||||
{{ locale.baseText('executionsList.retryWithCurrentlySavedWorkflow') }}
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
v-if="isRetriable"
|
||||
@@ -286,7 +289,7 @@ async function handleActionItemClick(commandData: Command) {
|
||||
command="retryOriginal"
|
||||
:disabled="!workflowPermissions.execute"
|
||||
>
|
||||
{{ i18n.baseText('executionsList.retryWithOriginalWorkflow') }}
|
||||
{{ locale.baseText('executionsList.retryWithOriginalWorkflow') }}
|
||||
</ElDropdownItem>
|
||||
<ElDropdownItem
|
||||
data-test-id="execution-delete-dropdown-item"
|
||||
@@ -294,7 +297,7 @@ async function handleActionItemClick(commandData: Command) {
|
||||
command="delete"
|
||||
:disabled="!workflowPermissions.update"
|
||||
>
|
||||
{{ i18n.baseText('generic.delete') }}
|
||||
{{ locale.baseText('generic.delete') }}
|
||||
</ElDropdownItem>
|
||||
</ElDropdownMenu>
|
||||
</template>
|
||||
@@ -304,126 +307,11 @@ async function handleActionItemClick(commandData: Command) {
|
||||
</template>
|
||||
|
||||
<style lang="scss" module>
|
||||
@import '@/styles/variables';
|
||||
|
||||
.executionListItem {
|
||||
--execution-list-item-background: var(--color-table-row-background);
|
||||
--execution-list-item-highlight-background: var(--color-table-row-highlight-background);
|
||||
color: var(--color-text-base);
|
||||
|
||||
td {
|
||||
background: var(--execution-list-item-background);
|
||||
}
|
||||
|
||||
&:nth-child(even) td {
|
||||
--execution-list-item-background: var(--color-table-row-even-background);
|
||||
}
|
||||
|
||||
&:hover td {
|
||||
background: var(--color-table-row-hover-background);
|
||||
}
|
||||
|
||||
td:first-child {
|
||||
width: 30px;
|
||||
padding: 0 var(--spacing-s) 0 0;
|
||||
|
||||
/*
|
||||
This is needed instead of table cell border because they are overlapping the sticky header
|
||||
*/
|
||||
&::before {
|
||||
content: '';
|
||||
display: inline-block;
|
||||
width: var(--spacing-4xs);
|
||||
height: 100%;
|
||||
vertical-align: middle;
|
||||
margin-right: var(--spacing-xs);
|
||||
}
|
||||
}
|
||||
|
||||
&.crashed td:first-child::before,
|
||||
&.error td:first-child::before {
|
||||
background: var(--execution-card-border-error);
|
||||
}
|
||||
|
||||
&.success td:first-child::before {
|
||||
background: var(--execution-card-border-success);
|
||||
}
|
||||
|
||||
&.new td:first-child::before {
|
||||
background: var(--execution-card-border-waiting);
|
||||
}
|
||||
|
||||
&.running td:first-child::before {
|
||||
background: var(--execution-card-border-running);
|
||||
}
|
||||
|
||||
&.waiting td:first-child::before {
|
||||
background: var(--execution-card-border-waiting);
|
||||
}
|
||||
|
||||
&.unknown td:first-child::before {
|
||||
background: var(--execution-card-border-unknown);
|
||||
}
|
||||
tr.dangerBg {
|
||||
background-color: rgba(215, 56, 58, 0.1);
|
||||
}
|
||||
|
||||
.link {
|
||||
color: var(--color-text-base);
|
||||
text-decoration: underline;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.statusColumn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.spinner {
|
||||
margin-right: var(--spacing-2xs);
|
||||
}
|
||||
|
||||
.status {
|
||||
line-height: 22.6px;
|
||||
text-align: center;
|
||||
font-size: var(--font-size-s);
|
||||
font-weight: var(--font-weight-bold);
|
||||
|
||||
.crashed &,
|
||||
.error & {
|
||||
color: var(--color-danger);
|
||||
}
|
||||
|
||||
.waiting & {
|
||||
color: var(--color-secondary);
|
||||
}
|
||||
|
||||
.success & {
|
||||
font-weight: var(--font-weight-normal);
|
||||
}
|
||||
|
||||
.new & {
|
||||
color: var(--execution-card-text-waiting);
|
||||
}
|
||||
|
||||
.running & {
|
||||
color: var(--color-warning);
|
||||
}
|
||||
|
||||
.unknown & {
|
||||
color: var(--color-background-dark);
|
||||
}
|
||||
}
|
||||
|
||||
.buttonCell {
|
||||
overflow: hidden;
|
||||
|
||||
button {
|
||||
transform: translateX(1000%);
|
||||
transition: transform 0s;
|
||||
|
||||
&:focus-visible,
|
||||
.executionListItem:hover & {
|
||||
transform: translateX(0);
|
||||
}
|
||||
}
|
||||
.capitalize {
|
||||
text-transform: capitalize;
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -773,10 +773,12 @@
|
||||
"executionsList.showMessage.retrySuccessfulTrue.title": "Retry successful",
|
||||
"executionsList.showMessage.stopExecution.message": "Execution ID {activeExecutionId}",
|
||||
"executionsList.showMessage.stopExecution.title": "Execution stopped",
|
||||
"executionsList.startedAt": "Started At",
|
||||
"executionsList.startedAt": "Started",
|
||||
"executionsList.trigger": "Triggered by",
|
||||
"executionsList.runTime": "Run time",
|
||||
"executionsList.startingSoon": "Starting soon",
|
||||
"executionsList.started": "{date} at {time}",
|
||||
"executionsList.id": "Execution ID",
|
||||
"executionsList.started": "{date}, {time}",
|
||||
"executionsList.id": "Exec. ID",
|
||||
"executionsList.status": "Status",
|
||||
"executionsList.statusCanceled": "Canceled",
|
||||
"executionsList.statusText": "{status} in {time}",
|
||||
|
||||
@@ -60,3 +60,75 @@ export const faTriangle: IconDefinition = {
|
||||
'M214.433 56C232.908 23.9999 279.096 24.0001 297.571 56L477.704 368C496.18 400 473.085 440 436.135 440H75.8685C38.918 440 15.8241 400 34.2993 368L214.433 56ZM256.002 144L131.294 360H380.709L256.002 144Z',
|
||||
],
|
||||
};
|
||||
|
||||
export const statusCompleted: IconDefinition = {
|
||||
prefix: 'fas',
|
||||
iconName: 'status-completed' as IconName,
|
||||
icon: [
|
||||
14,
|
||||
14,
|
||||
[],
|
||||
'',
|
||||
'M 14 7 C 14 10.866 10.866 14 7 14 C 3.134 14 0 10.866 0 7 C 0 3.134 3.134 0 7 0 C 10.866 0 14 3.134 14 7 Z M 2.575 7.728 L 5.782 10.935 L 11.489 5.228 L 10.075 3.814 L 5.782 8.107 L 3.989 6.314 L 2.575 7.728 Z',
|
||||
],
|
||||
};
|
||||
|
||||
export const statusWaiting: IconDefinition = {
|
||||
prefix: 'fas',
|
||||
iconName: 'status-waiting' as IconName,
|
||||
icon: [
|
||||
14,
|
||||
14,
|
||||
[],
|
||||
'',
|
||||
'M7 14C10.866 14 14 10.866 14 7C14 3.13401 10.866 0 7 0C3.13401 0 0 3.13401 0 7C0 10.866 3.13401 14 7 14ZM7 12C4.23858 12 2 9.76142 2 7C2 4.23858 4.23858 2 7 2C9.76142 2 12 4.23858 12 7C12 9.76142 9.76142 12 7 12ZM6 3V8H11C11 5.23858 8.76142 3 6 3Z',
|
||||
],
|
||||
};
|
||||
|
||||
export const statusError: IconDefinition = {
|
||||
prefix: 'fas',
|
||||
iconName: 'status-error' as IconName,
|
||||
icon: [
|
||||
14,
|
||||
14,
|
||||
[],
|
||||
'',
|
||||
'M 4.207 2.793 L 7 5.586 L 9.793 2.793 L 11.207 4.207 L 8.414 7 L 11.207 9.793 L 9.793 11.207 L 7 8.414 L 4.207 11.207 L 2.793 9.793 L 5.586 7 L 2.793 4.207 L 4.207 2.793 Z M 7 0 C 3.134 0 0 3.134 0 7 C 0 10.866 3.134 14 7 14 C 10.866 14 14 10.866 14 7 C 14 3.134 10.866 0 7 0 Z',
|
||||
],
|
||||
};
|
||||
|
||||
export const statusCanceled: IconDefinition = {
|
||||
prefix: 'fas',
|
||||
iconName: 'status-canceled' as IconName,
|
||||
icon: [
|
||||
14,
|
||||
14,
|
||||
[],
|
||||
'',
|
||||
'M 14 7 C 14 10.866 10.866 14 7 14 C 3.134 14 0 10.866 0 7 C 0 3.134 3.134 0 7 0 C 10.866 0 14 3.134 14 7 Z M 11.243 6 L 2.758 6 L 2.758 8 L 11.243 8 L 11.243 6 Z',
|
||||
],
|
||||
};
|
||||
|
||||
export const statusNew: IconDefinition = {
|
||||
prefix: 'fas',
|
||||
iconName: 'status-new' as IconName,
|
||||
icon: [
|
||||
14,
|
||||
14,
|
||||
[],
|
||||
'',
|
||||
'M 14 7.006 C 14 8.867 13.162 10.744 11.95 11.956 C 10.738 13.168 8.861 14.006 7 14.006 C 5.139 14.006 3.262 13.168 2.05 11.956 C 0.838 10.744 0 8.867 0 7.006 C 0 5.145 0.838 3.268 2.05 2.056 C 3.262 0.844 5.139 0.006 7 0.006 C 8.861 0.006 10.738 0.844 11.95 2.056 C 13.162 3.268 14 5.145 14 7.006 Z M 10.536 3.47 C 9.576 2.511 8.453 2.006 7 2.006 C 5.547 2.006 4.424 2.511 3.464 3.47 C 2.505 4.43 2 5.553 2 7.006 C 2 8.459 2.505 9.582 3.464 10.542 C 4.424 11.501 5.547 12.006 7 12.006 C 8.453 12.006 9.576 11.501 10.536 10.542 C 11.495 9.582 12 8.459 12 7.006 C 12 5.553 11.495 4.43 10.536 3.47 Z',
|
||||
],
|
||||
};
|
||||
|
||||
export const statusUnknown: IconDefinition = {
|
||||
prefix: 'fas',
|
||||
iconName: 'status-unknown' as IconName,
|
||||
icon: [
|
||||
14,
|
||||
14,
|
||||
[],
|
||||
'',
|
||||
'M13.8668 8.36613L11.9048 7.978C11.967 7.66329 12 7.33649 12 7C12 6.66351 11.967 6.3367 11.9048 6.022L13.8668 5.63387C13.9542 6.07571 14 6.5325 14 7C14 7.4675 13.9542 7.92429 13.8668 8.36613ZM12.821 3.11069L11.159 4.22333C10.7934 3.67721 10.3228 3.2066 9.77667 2.84098L10.8893 1.17904C11.6527 1.6901 12.3099 2.34733 12.821 3.11069ZM8.36613 0.133238L7.978 2.09521C7.66329 2.03296 7.33649 2 7 2C6.66351 2 6.3367 2.03296 6.022 2.09521L5.63387 0.133238C6.07571 0.0458286 6.5325 0 7 0C7.4675 0 7.92429 0.0458285 8.36613 0.133238ZM3.11069 1.17904L4.22333 2.84098C3.67721 3.2066 3.2066 3.67721 2.84098 4.22333L1.17904 3.11069C1.6901 2.34733 2.34733 1.6901 3.11069 1.17904ZM0.133238 5.63387C0.0458285 6.07571 0 6.5325 0 7C0 7.4675 0.0458286 7.92429 0.133238 8.36613L2.09521 7.978C2.03296 7.6633 2 7.33649 2 7C2 6.66351 2.03296 6.33671 2.09521 6.022L0.133238 5.63387ZM1.17904 10.8893L2.84098 9.77667C3.2066 10.3228 3.67721 10.7934 4.22333 11.159L3.11069 12.821C2.34733 12.3099 1.6901 11.6527 1.17904 10.8893ZM5.63387 13.8668L6.022 11.9048C6.33671 11.967 6.66351 12 7 12C7.33649 12 7.6633 11.967 7.978 11.9048L8.36613 13.8668C7.92429 13.9542 7.4675 14 7 14C6.5325 14 6.07571 13.9542 5.63387 13.8668ZM10.8893 12.821L9.77667 11.159C10.3228 10.7934 10.7934 10.3228 11.159 9.77667L12.821 10.8893C12.3099 11.6527 11.6527 12.3099 10.8893 12.821Z',
|
||||
],
|
||||
};
|
||||
|
||||
@@ -172,7 +172,19 @@ import {
|
||||
faMinusCircle,
|
||||
faAdjust,
|
||||
} from '@fortawesome/free-solid-svg-icons';
|
||||
import { faVariable, faXmark, faVault, faRefresh, faTriangle } from './custom';
|
||||
import {
|
||||
faVariable,
|
||||
faXmark,
|
||||
faVault,
|
||||
faRefresh,
|
||||
faTriangle,
|
||||
statusCompleted,
|
||||
statusWaiting,
|
||||
statusError,
|
||||
statusCanceled,
|
||||
statusNew,
|
||||
statusUnknown,
|
||||
} from './custom';
|
||||
import { faStickyNote } from '@fortawesome/free-regular-svg-icons';
|
||||
import { FontAwesomeIcon } from '@fortawesome/vue-fontawesome';
|
||||
|
||||
@@ -357,6 +369,13 @@ export const FontAwesomePlugin: Plugin = {
|
||||
addIcon(faRefresh);
|
||||
addIcon(faMinusCircle);
|
||||
addIcon(faAdjust);
|
||||
// statuses
|
||||
addIcon(statusCompleted);
|
||||
addIcon(statusWaiting);
|
||||
addIcon(statusError);
|
||||
addIcon(statusCanceled);
|
||||
addIcon(statusNew);
|
||||
addIcon(statusUnknown);
|
||||
|
||||
app.component('FontAwesomeIcon', FontAwesomeIcon);
|
||||
},
|
||||
|
||||
@@ -319,5 +319,6 @@ export const useExecutionsStore = defineStore('executions', () => {
|
||||
addExecution,
|
||||
resetData,
|
||||
reset,
|
||||
itemsPerPage,
|
||||
};
|
||||
});
|
||||
|
||||
@@ -1,40 +1,40 @@
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref, useTemplateRef, onMounted } from 'vue';
|
||||
import VariablesForm from '@/components/VariablesForm.vue';
|
||||
import VariablesUsageBadge from '@/components/VariablesUsageBadge.vue';
|
||||
import { useDocumentTitle } from '@/composables/useDocumentTitle';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useMessage } from '@/composables/useMessage';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useEnvironmentsStore } from '@/stores/environments.ee.store';
|
||||
import { useSettingsStore } from '@/stores/settings.store';
|
||||
import { useSourceControlStore } from '@/stores/sourceControl.store';
|
||||
import { useUIStore } from '@/stores/ui.store';
|
||||
import { useUsersStore } from '@/stores/users.store';
|
||||
import { useI18n } from '@/composables/useI18n';
|
||||
import { useTelemetry } from '@/composables/useTelemetry';
|
||||
import { useToast } from '@/composables/useToast';
|
||||
import { useMessage } from '@/composables/useMessage';
|
||||
import { useDocumentTitle } from '@/composables/useDocumentTitle';
|
||||
import { computed, onMounted, ref, useTemplateRef } from 'vue';
|
||||
import { useRoute, useRouter, type LocationQueryRaw } from 'vue-router';
|
||||
import VariablesForm from '@/components/VariablesForm.vue';
|
||||
import VariablesUsageBadge from '@/components/VariablesUsageBadge.vue';
|
||||
|
||||
import ResourcesListLayout, {
|
||||
type Resource,
|
||||
type BaseFilters,
|
||||
type Resource,
|
||||
type VariableResource,
|
||||
} from '@/components/layouts/ResourcesListLayout.vue';
|
||||
|
||||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||
import { EnterpriseEditionFeature, MODAL_CONFIRM } from '@/constants';
|
||||
import type { DatatableColumn, EnvironmentVariable } from '@/Interface';
|
||||
import { uid } from '@n8n/design-system/utils';
|
||||
import { getResourcePermissions } from '@/permissions';
|
||||
import { usePageRedirectionHelper } from '@/composables/usePageRedirectionHelper';
|
||||
import {
|
||||
N8nActionBox,
|
||||
N8nBadge,
|
||||
N8nButton,
|
||||
N8nCheckbox,
|
||||
N8nInputLabel,
|
||||
N8nTooltip,
|
||||
} from '@n8n/design-system';
|
||||
import { uid } from '@n8n/design-system/utils';
|
||||
import { useAsyncState } from '@vueuse/core';
|
||||
import { pickBy } from 'lodash-es';
|
||||
import {
|
||||
N8nButton,
|
||||
N8nTooltip,
|
||||
N8nActionBox,
|
||||
N8nInputLabel,
|
||||
N8nCheckbox,
|
||||
N8nBadge,
|
||||
} from '@n8n/design-system';
|
||||
|
||||
const settingsStore = useSettingsStore();
|
||||
const environmentsStore = useEnvironmentsStore();
|
||||
|
||||
Reference in New Issue
Block a user