n8n/packages/editor-ui/src/components/ExecutionsView/ExecutionCard.vue
Milorad FIlipović be7672a177
fix(editor): Add 'Stop execution' button to execution preview (#4632)
*  Adding `Stop execution` button to execution preview
*  Added execution timer for running executions
* 💄 Adjusting spinner size and text color
* 🔥 Removing excessive popup error message when opening failed executions preview
* 🐛 Handling execution stopping when workflow is not saving manual executions
2022-11-17 17:34:55 +01:00

212 lines
5.5 KiB
Vue

<template>
<div
:class="{
['execution-card']: true,
[$style.executionCard]: true,
[$style.active]: isActive,
[$style[executionUIDetails.name]]: true,
[$style.highlight]: highlight,
}"
>
<router-link
:class="$style.executionLink"
:to="{ name: VIEWS.EXECUTION_PREVIEW, params: { workflowId: currentWorkflow, executionId: execution.id }}"
>
<div :class="$style.description">
<n8n-text color="text-dark" :bold="true" size="medium">{{ executionUIDetails.startTime }}</n8n-text>
<div :class="$style.executionStatus">
<n8n-spinner v-if="executionUIDetails.name === 'running'" size="small" :class="[$style.spinner, 'mr-4xs']"/>
<n8n-text :class="$style.statusLabel" size="small">{{ executionUIDetails.label }}</n8n-text>
<n8n-text v-if="executionUIDetails.name === 'running'" :color="isActive? 'text-dark' : 'text-base'" size="small">
{{ $locale.baseText('executionDetails.runningTimeRunning') }}
<execution-time :start-time="execution.startedAt"/>
</n8n-text>
<n8n-text v-else-if="executionUIDetails.name !== 'waiting' && executionUIDetails.name !== 'unknown'" :color="isActive? 'text-dark' : 'text-base'" size="small">
{{ $locale.baseText('executionDetails.runningTimeFinished', { interpolate: { time: executionUIDetails.runningTime } }) }}
</n8n-text>
</div>
<div v-if="execution.mode === 'retry'">
<n8n-text :color="isActive? 'text-dark' : 'text-base'" size="small">
{{ $locale.baseText('executionDetails.retry') }} #{{ execution.retryOf }}
</n8n-text>
</div>
</div>
<div :class="$style.icons">
<n8n-action-dropdown
v-if="executionUIDetails.name === 'error'"
:class="[$style.icon, $style.retry]"
:items="retryExecutionActions"
activatorIcon="redo"
@select="onRetryMenuItemSelect"
/>
<font-awesome-icon
v-if="execution.mode === 'manual'"
:class="[$style.icon, $style.manual]"
:title="$locale.baseText('executionsList.manual')"
icon="flask"
/>
</div>
</router-link>
</div>
</template>
<script lang="ts">
import { IExecutionsSummary } from '@/Interface';
import mixins from 'vue-typed-mixins';
import { executionHelpers, IExecutionUIData } from '../mixins/executionsHelpers';
import { VIEWS } from '../../constants';
import { showMessage } from '../mixins/showMessage';
import { restApi } from '../mixins/restApi';
import ExecutionTime from '@/components/ExecutionTime.vue';
export default mixins(
executionHelpers,
showMessage,
restApi,
).extend({
name: 'execution-card',
components: {
ExecutionTime,
},
data() {
return {
VIEWS,
};
},
props: {
execution: {
type: Object as () => IExecutionsSummary,
required: true,
},
highlight: {
type: Boolean,
default: false,
},
},
computed: {
retryExecutionActions(): object[] {
return [
{ id: 'current-workflow', label: this.$locale.baseText('executionsList.retryWithCurrentlySavedWorkflow') },
{ id: 'original-workflow', label: this.$locale.baseText('executionsList.retryWithOriginalWorkflow') },
];
},
executionUIDetails(): IExecutionUIData {
return this.getExecutionUIDetails(this.execution);
},
isActive(): boolean {
return this.execution.id === this.$route.params.executionId;
},
},
methods: {
onRetryMenuItemSelect(action: string): void {
this.$emit('retryExecution', { execution: this.execution, command: action });
},
},
});
</script>
<style module lang="scss">
.executionCard {
display: flex;
padding-right: var(--spacing-2xs);
&.active {
padding: 0 var(--spacing-2xs) var(--spacing-2xs) 0;
border-left: var(--spacing-4xs) var(--border-style-base) transparent !important;
.executionStatus {
color: var(--color-text-dark) !important;
}
}
& + &.active { padding-top: var(--spacing-2xs); }
&:hover, &.active {
.executionLink {
background-color: var(--color-foreground-base);
}
}
&.running {
.spinner {
position: relative;
top: 1px;
}
&, & .executionLink {
border-left: var(--spacing-4xs) var(--border-style-base) hsl(var(--color-warning-h), 94%, 80%);
}
.statusLabel, .spinner { color: var(--color-warning); }
}
&.success {
&, & .executionLink {
border-left: var(--spacing-4xs) var(--border-style-base) hsl(var(--color-success-h), 60%, 70%);
}
}
&.waiting {
&, & .executionLink {
border-left: var(--spacing-4xs) var(--border-style-base) hsl(var(--color-secondary-h), 94%, 80%);
}
.statusLabel { color: var(--color-secondary); }
}
&.error {
&, & .executionLink {
border-left: var(--spacing-4xs) var(--border-style-base) hsl(var(--color-danger-h), 94%, 80%);
}
.statusLabel { color: var(--color-danger ); }
}
&.unknown {
&, & .executionLink {
border-left: var(--spacing-4xs) var(--border-style-base) var(--color-text-light);
}
}
}
.executionLink {
display: flex;
width: 100%;
align-items: center;
justify-content: space-between;
color: var(--color-text-base);
font-size: var(--font-size-xs);
padding: var(--spacing-xs);
padding-right: var(--spacing-s);
border-radius: var(--border-radius-base);
position: relative;
left: calc(-1 * var(--spacing-4xs)); // Hide link border under card border so it's not visible when not hovered
&:active {
.icon, .statusLabel {
color: var(--color-text-base);;
}
}
}
.icons {
display: flex;
align-items: baseline;
}
.icon {
font-size: var(--font-size-s);
&.retry {
svg {
color: var(--color-primary);
}
}
&.manual {
position: relative;
top: 1px;
}
& + & {
margin-left: var(--spacing-2xs);
}
}
</style>