/** * Workflow Catalog * * Single source of truth for every workflow type known to the frontend: * what it's called, what category/description/icons it has, how many steps * it has and what they're called, what its initial payload looks like, and * which Vue component renders it. * * Each workflow has a fully co-located component + metadata as a static * `meta` option on the component (`Component.meta`, right next to * `name`/`props`/etc.) in `@/components/workflow/workflows/*.vue` - this * file simply imports those components and reads `.meta` off of them to * build the catalog below. * * This replaces the previous design of a parallel `BaseWorkflow` class * hierarchy (metadata) plus a separate per-step `ComponentRegistry.js` * (components) - both concerns now live in one flat array with each * component responsible for its own metadata and UI implementation. */ import FotoFirstBulkImportWorkflow from '@/components/workflow/workflows/FotoFirstBulkImportWorkflow.vue'; import BulkItemImportWorkflow from '@/components/workflow/workflows/BulkItemImportWorkflow.vue'; import InventoryAuditWorkflow from '@/components/workflow/workflows/InventoryAuditWorkflow.vue'; import StorageOptimizationWorkflow from '@/components/workflow/workflows/StorageOptimizationWorkflow.vue'; import MaintenanceScheduleWorkflow from '@/components/workflow/workflows/MaintenanceScheduleWorkflow.vue'; import ExpiryCheckWorkflow from '@/components/workflow/workflows/ExpiryCheckWorkflow.vue'; import BackupRestoreWorkflow from '@/components/workflow/workflows/BackupRestoreWorkflow.vue'; /** * Workflows with a fully co-located component + metadata. */ const workflows = [ {...FotoFirstBulkImportWorkflow.meta, component: FotoFirstBulkImportWorkflow}, {...BulkItemImportWorkflow.meta, component: BulkItemImportWorkflow}, {...InventoryAuditWorkflow.meta, component: InventoryAuditWorkflow}, {...StorageOptimizationWorkflow.meta, component: StorageOptimizationWorkflow}, {...MaintenanceScheduleWorkflow.meta, component: MaintenanceScheduleWorkflow}, {...ExpiryCheckWorkflow.meta, component: ExpiryCheckWorkflow}, {...BackupRestoreWorkflow.meta, component: BackupRestoreWorkflow}, ]; /** * Get every workflow in the catalog. * @returns {Array} */ export function getAllWorkflows() { return workflows; } /** * Get a single workflow definition by id. * @param {string} id * @returns {Object|undefined} */ export function getWorkflow(slug) { return workflows.find(workflow => workflow.slug === slug); } /** * Get the Vue component implementing a workflow's UI, if any. * @param {string} id * @returns {Object|null} */ export function getWorkflowComponent(id) { return getWorkflow(id)?.component || null; } /** * Get all workflows belonging to a category. * @param {string} category * @returns {Array} */ export function getWorkflowsByCategory(category) { return workflows.filter(workflow => workflow.category === category); } /** * Get all unique categories present in the catalog. * @returns {Array} */ export function getWorkflowCategories() { return [...new Set(workflows.map(workflow => workflow.category))]; } /** * Build the payload sent to the backend to start a new instance of a * workflow, merging the common `workflow_config` metadata block with the * workflow's own initial payload fields. * @param {Object} workflow - A workflow definition, e.g. from getWorkflow() * @returns {Object} */ export function buildWorkflowApiPayload(workflow) { const ownPayload = workflow.getInitialPayload ? workflow.getInitialPayload() : {}; return { title: workflow.title, slug: workflow.slug, state: 'running', current_step: 1, total_steps: workflow.stepDefinitions.length, payload: { ...ownPayload } }; } /** * The backend stores WorkflowInstance.payload as an opaque string - it never * parses or understands it as JSON. The frontend is fully responsible for * serializing it before sending and deserializing it after receiving. */ export function serializeWorkflowPayload(workflow) { console.log(workflow); if (!workflow || !('payload' in workflow)) return workflow; return {...workflow, payload: JSON.stringify(workflow.payload ?? {})}; } export function deserializeWorkflowPayload(workflow) { if (!workflow) return workflow; let payload = {}; try { payload = workflow.payload ? JSON.parse(workflow.payload) : {}; } catch (e) { console.error('Failed to parse workflow payload JSON:', e); } return {...workflow, payload}; } export default { getAllWorkflows, getWorkflow, getWorkflowComponent, getWorkflowsByCategory, getWorkflowCategories, buildWorkflowApiPayload, serializeWorkflowPayload, deserializeWorkflowPayload };