This commit is contained in:
j3d1 2026-08-16 15:15:38 +02:00
parent 6fcdd1eefa
commit c0f70004eb
21 changed files with 1855 additions and 260 deletions

View file

@ -16,49 +16,51 @@
<div class="table-responsive">
<table class="table table-hover">
<thead>
<tr>
<th>Workflow</th>
<th>Status</th>
<th>Progress</th>
<th>Started</th>
<th>Actions</th>
</tr>
<tr>
<th>Workflow</th>
<th>Status</th>
<th>Progress</th>
<th>Started</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
<tr v-for="workflow in activeWorkflows" :key="workflow.id">
<td>
<strong>{{ getWorkflowDisplayName(workflow) }}</strong>
<br>
<small class="text-muted">{{ workflow.payload?.workflow_config?.category || 'System' }}</small>
</td>
<td>
<tr v-for="workflow in activeWorkflows" :key="workflow.id">
<td>
<strong>{{ getWorkflowDisplayName(workflow) }}</strong>
<br>
<small class="text-muted">{{
workflow.payload?.workflow_config?.category || 'System'
}}</small>
</td>
<td>
<span :class="getStatusBadgeClass(workflow.state)">
{{ workflow.status_display || workflow.state }}
</span>
</td>
<td>
<div class="progress" style="height: 8px;">
<div class="progress-bar"
:style="{ width: workflow.progress_percentage + '%' }"
:class="getProgressBarClass(workflow.state)">
</div>
</td>
<td>
<div class="progress" style="height: 8px;">
<div class="progress-bar"
:style="{ width: workflow.progress_percentage + '%' }"
:class="getProgressBarClass(workflow.state)">
</div>
<small class="text-muted">{{ workflow.progress_percentage }}%</small>
</td>
<td>{{ formatDate(workflow.started_at) }}</td>
<td>
<button class="btn btn-sm btn-outline-primary me-1"
@click="viewWorkflowDetails(workflow)"
:disabled="loading">
<b-icon-eye></b-icon-eye>
</button>
<button class="btn btn-sm btn-outline-danger"
@click="abortWorkflowInstance(workflow)"
:disabled="loading">
<b-icon-x-circle></b-icon-x-circle>
</button>
</td>
</tr>
</div>
<small class="text-muted">{{ workflow.progress_percentage }}%</small>
</td>
<td>{{ formatDate(workflow.started_at) }}</td>
<td>
<button class="btn btn-sm btn-outline-primary me-1"
@click="viewWorkflowDetails(workflow)"
:disabled="loading">
<b-icon-eye></b-icon-eye>
</button>
<button class="btn btn-sm btn-outline-danger"
@click="abortWorkflowInstance(workflow)"
:disabled="loading">
<b-icon-x-circle></b-icon-x-circle>
</button>
</td>
</tr>
</tbody>
</table>
</div>
@ -77,21 +79,25 @@
</div>
<div class="card-body">
<div class="row">
<div v-for="workflow in availableWorkflows" :key="workflow.id" class="col-lg-4 col-md-6 mb-3">
<div v-for="workflow in availableWorkflows" :key="workflow.id"
class="col-lg-4 col-md-6 mb-3">
<div class="card h-100 workflow-card">
<div class="card-body d-flex flex-column">
<div class="d-flex align-items-center mb-3">
<template v-for="(icon, index) in workflow.icons" :key="icon">
<div class="workflow-icon me-3">
<component :is="icon" class="text-primary" style="font-size: 1.5rem;"></component>
<component :is="icon" class="text-primary"
style="font-size: 1.5rem;"></component>
</div>
<!-- Add arrow between icons, but not after the last one -->
<div v-if="index < workflow.icons.length - 1" class="workflow-arrow me-3">
<b-icon-arrow-right class="text-muted" style="font-size: 1rem;"></b-icon-arrow-right>
<div v-if="index < workflow.icons.length - 1"
class="workflow-arrow me-3">
<b-icon-arrow-right class="text-muted"
style="font-size: 1rem;"></b-icon-arrow-right>
</div>
</template>
<div class="workflow-header">
<h6 class="card-title mb-1">{{ workflow.name }}</h6>
<h6 class="card-title mb-1">{{ workflow.title }}</h6>
<small class="text-muted">{{ workflow.category }}</small>
</div>
</div>
@ -105,10 +111,9 @@
<span class="badge bg-light text-dark">{{ workflow.steps }} steps</span>
</div>
<button class="btn btn-primary w-100"
@click="startWorkflow(workflow)"
:disabled="isWorkflowRunning(workflow.id)">
@click="startWorkflow(workflow)">
<b-icon-play-fill class="me-1"></b-icon-play-fill>
{{ isWorkflowRunning(workflow.id) ? 'Running...' : 'Start Workflow' }}
Start Workflow
</button>
</div>
</div>
@ -127,8 +132,8 @@
<script>
import * as BIcons from "bootstrap-icons-vue";
import BaseLayout from "@/components/BaseLayout.vue";
import { mapState, mapActions } from 'vuex';
import { getAllWorkflows, getWorkflow, buildWorkflowApiPayload } from '@/workflows.js';
import {mapState, mapActions} from 'vuex';
import {getAllWorkflows, getWorkflow, buildWorkflowApiPayload} from '@/workflows.js';
export default {
name: 'Workflows',
@ -213,15 +218,8 @@ export default {
minute: '2-digit'
}).format(date);
},
isWorkflowRunning(workflowId) {
return this.activeWorkflows.some(active =>
active.name === workflowId &&
active.state === 'running'
);
},
getWorkflowDisplayName(workflow) {
// `name` doubles as the workflow type identifier (e.g. 'import-items').
return getWorkflow(workflow.name)?.name || workflow.name;
return getWorkflow(workflow.slug)?.title;
},
async startWorkflow(workflow) {
try {
@ -232,11 +230,10 @@ export default {
const workflowData = buildWorkflowApiPayload(workflow);
const newWorkflow = await this.createWorkflow(workflowData);
console.log('Workflow started successfully:', newWorkflow);
// Immediately navigate to the workflow detail view
// Get the first step from the workflow definition
const firstStep = workflow.stepDefinitions?.[0]?.step || "initial";
const firstStep = workflow.stepDefinitions?.[0]?.step || 1;
this.$router.push({
name: 'workflow-detail',
params: {
@ -257,9 +254,9 @@ export default {
// Navigate to the workflow detail view
// Use the workflow's current step if available, otherwise use the first step
const currentStep = workflow.current_step ||
workflow.payload?.current_step ||
getWorkflow(workflow.name)?.stepDefinitions?.[0]?.step ||
"initial";
workflow.payload?.current_step ||
getWorkflow(workflow.name)?.stepDefinitions?.[0]?.step ||
"initial";
this.$router.push({
name: 'workflow-detail',