This commit is contained in:
j3d1 2026-08-01 22:20:45 +02:00
parent cfcc2c15d3
commit 4f2fe011c0
28 changed files with 3499 additions and 2922 deletions

View file

@ -1,409 +1,110 @@
/**
* Common Workflow Interface
*
* All workflow classes should implement this interface:
*
* interface IWorkflow {
* id: string; // Unique identifier for the workflow type
* name: string; // Display name for the workflow
* category: string; // Category grouping (e.g., 'Data Management', 'Inventory Management')
* description: string; // Detailed description of what the workflow does
* icons: Array<string>; // Array of Bootstrap icon component names
* estimatedDuration: string; // Human-readable duration estimate
* steps: number; // Total number of steps in the workflow
*
* // Methods
* validate(): boolean; // Validate if workflow can be started
* getStepDefinitions(): Array; // Get array of step definitions
* getInitialPayload(): Object; // Get initial payload structure
* }
* 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';
/**
* Base workflow class that all workflows extend
* Workflows with a fully co-located component + metadata.
*/
class BaseWorkflow {
constructor(id, name, category, description, icons, estimatedDuration, steps) {
this.id = id;
this.name = name;
this.category = category;
this.description = description;
// Ensure icon is always an array - convert single string to array if needed
this.icons = Array.isArray(icons) ? icons : [icons];
this.estimatedDuration = estimatedDuration;
this.steps = steps;
}
/**
* Validate if the workflow can be started
* Override in subclasses for specific validation logic
*/
validate() {
return true;
}
/**
* Get the initial payload structure for this workflow
* Override in subclasses to provide workflow-specific payload
*/
getInitialPayload() {
return {
const implementedWorkflows = [
{ ...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 },
];
/**
* The full workflow catalog: every workflow type known to the frontend,
* whether it has a custom UI or not.
*/
const workflows = implementedWorkflows;
/**
* Get every workflow in the catalog.
* @returns {Array<Object>}
*/
export function getAllWorkflows() {
return workflows;
}
/**
* Get a single workflow definition by id.
* @param {string} id
* @returns {Object|undefined}
*/
export function getWorkflow(id) {
return workflows.find(workflow => workflow.id === id);
}
/**
* 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<Object>}
*/
export function getWorkflowsByCategory(category) {
return workflows.filter(workflow => workflow.category === category);
}
/**
* Get all unique categories present in the catalog.
* @returns {Array<string>}
*/
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 {
name: workflow.name,
workflow_type: workflow.id,
state: 'running',
current_step: 1,
total_steps: workflow.stepDefinitions.length,
payload: {
workflow_config: {
name: this.name,
description: this.description,
category: this.category,
estimated_duration: this.estimatedDuration
}
};
}
/**
* Get step definitions for this workflow
* Override in subclasses to provide workflow-specific steps
*/
getStepDefinitions() {
return [];
}
/**
* Convert workflow to API-compatible format
*/
toApiFormat() {
return {
name: this.name,
workflow_type: this.id,
state: 'running',
current_step: 1,
total_steps: this.steps,
payload: this.getInitialPayload()
};
}
}
/**
* Foto First Import Workflow
* Captures photos via mobile camera or upload, then sequentially enters details for each item
*/
class FotoFirstImportWorkflow extends BaseWorkflow {
constructor() {
super(
'foto-first-bulk-import',
'Foto First Bulk Import',
'Data Management',
'Capture unlimited photos via mobile camera or upload images, then sequentially enter details for each item.',
['b-icon-camera', 'b-icon-pencil-square'],
'10-60 minutes',
4
);
}
getStepDefinitions() {
return [
{ step: 1, name: 'Photo Capture', description: 'Capture or upload item photos' },
{ step: 2, name: 'Image Processing', description: 'Process and optimize images' },
{ step: 3, name: 'Item Details Entry', description: 'Enter details for each photographed item' },
{ step: 4, name: 'Import Completion', description: 'Finalize and save imported items' }
];
}
getInitialPayload() {
return {
...super.getInitialPayload(),
photos: [],
processing_options: {
auto_rotate: true,
compress: true,
max_width: 1920,
max_height: 1080
}
};
}
}
/**
* Bulk Item Import Workflow
* Imports multiple inventory items from CSV or Excel files with validation
*/
class BulkItemImportWorkflow extends BaseWorkflow {
constructor() {
super(
'import-items',
'Bulk Item Import',
'Data Management',
'Import multiple inventory items from CSV or Excel files with validation.',
['b-icon-upload', 'b-icon-file-earmark-spreadsheet', 'b-icon-list-check'],
'20-60 minutes',
7
);
}
getStepDefinitions() {
return [
{ step: 1, name: 'File Upload', description: 'Upload CSV or Excel file' },
{ step: 2, name: 'Parse Data', description: 'Parse and analyze file contents' },
{ step: 3, name: 'Validate Format', description: 'Validate data format and structure' },
{ step: 4, name: 'Data Validation', description: 'Validate individual item data' },
{ step: 5, name: 'Conflict Resolution', description: 'Resolve any data conflicts' },
{ step: 6, name: 'Import Items', description: 'Import validated items into system' },
{ step: 7, name: 'Generate Report', description: 'Generate import summary report' }
];
}
getInitialPayload() {
return {
...super.getInitialPayload(),
import_options: {
file_type: null,
skip_duplicates: true,
update_existing: false,
validate_required_fields: true
name: workflow.name,
description: workflow.description,
category: workflow.category,
estimated_duration: workflow.estimatedDuration
},
mapping: {},
validation_results: []
};
}
validate() {
// Could add validation for file format, required permissions, etc.
return true;
}
}
/**
* Inventory Audit Workflow
* Performs a complete audit of inventory items, checking quantities, locations, and conditions
*/
class InventoryAuditWorkflow extends BaseWorkflow {
constructor() {
super(
'inventory-audit',
'Inventory Audit',
'Inventory Management',
'Perform a complete audit of your inventory items, checking quantities, locations, and conditions.',
['b-icon-list-ul'],
'2-4 hours',
8
);
}
getStepDefinitions() {
return [
{ step: 1, name: 'Initialize Audit', description: 'Set up audit parameters and scope' },
{ step: 2, name: 'Generate Item List', description: 'Create list of items to audit' },
{ step: 3, name: 'Location Verification', description: 'Verify item locations' },
{ step: 4, name: 'Quantity Count', description: 'Count physical quantities' },
{ step: 5, name: 'Condition Assessment', description: 'Assess item conditions' },
{ step: 6, name: 'Discrepancy Detection', description: 'Identify discrepancies' },
{ step: 7, name: 'Report Generation', description: 'Generate audit report' },
{ step: 8, name: 'Finalize Audit', description: 'Complete and archive audit' }
];
}
validate() {
// Add specific validation logic for inventory audit
return true;
}
}
/**
* Storage Optimization Workflow
* Analyzes and reorganizes storage locations for maximum efficiency and accessibility
*/
class StorageOptimizationWorkflow extends BaseWorkflow {
constructor() {
super(
'storage-optimization',
'Storage Optimization',
'Storage Management',
'Analyze and reorganize storage locations for maximum efficiency and accessibility.',
['b-icon-boxes', 'b-icon-diagram-3', 'b-icon-archive'],
'1-2 hours',
5
);
}
getStepDefinitions() {
return [
{ step: 1, name: 'Analyze Current Layout', description: 'Assess current storage efficiency' },
{ step: 2, name: 'Identify Optimization Opportunities', description: 'Find areas for improvement' },
{ step: 3, name: 'Plan Reorganization', description: 'Create optimization plan' },
{ step: 4, name: 'Execute Changes', description: 'Implement storage changes' },
{ step: 5, name: 'Validate Results', description: 'Verify optimization results' }
];
}
}
/**
* Maintenance Schedule Workflow
* Creates and executes maintenance schedules for tools and equipment
*/
class MaintenanceScheduleWorkflow extends BaseWorkflow {
constructor() {
super(
'maintenance-schedule',
'Maintenance Schedule',
'Tool Maintenance',
'Create and execute maintenance schedules for tools and equipment.',
['b-icon-tools', 'b-icon-calendar'],
'30 minutes',
4
);
}
getStepDefinitions() {
return [
{ step: 1, name: 'Identify Equipment', description: 'Select tools and equipment for maintenance' },
{ step: 2, name: 'Create Schedule', description: 'Define maintenance intervals and tasks' },
{ step: 3, name: 'Assign Responsibilities', description: 'Assign maintenance tasks to users' },
{ step: 4, name: 'Activate Schedule', description: 'Enable automatic maintenance reminders' }
];
}
}
/**
* Expiry Date Check Workflow
* Identifies and handles items approaching or past their expiry dates
*/
class ExpiryCheckWorkflow extends BaseWorkflow {
constructor() {
super(
'expiry-check',
'Expiry Date Check',
'Quality Control',
'Identify and handle items approaching or past their expiry dates.',
['b-icon-clock-history', 'b-icon-exclamation-triangle'],
'45 minutes',
6
);
}
getStepDefinitions() {
return [
{ step: 1, name: 'Scan Expiry Dates', description: 'Check all items for expiry information' },
{ step: 2, name: 'Identify Critical Items', description: 'Find expired and soon-to-expire items' },
{ step: 3, name: 'Assess Item Condition', description: 'Evaluate condition of critical items' },
{ step: 4, name: 'Generate Action Plan', description: 'Create disposal or usage recommendations' },
{ step: 5, name: 'Execute Actions', description: 'Implement recommended actions' },
{ step: 6, name: 'Update Records', description: 'Update item statuses and records' }
];
}
getInitialPayload() {
return {
...super.getInitialPayload(),
check_parameters: {
warning_days: 30,
include_no_expiry: false,
categories: []
}
};
}
}
/**
* Data Backup Workflow
* Creates a comprehensive backup of inventory and settings data
*/
class DataBackupWorkflow extends BaseWorkflow {
constructor() {
super(
'backup-restore',
'Data Backup',
'System Maintenance',
'Create a comprehensive backup of your inventory and settings data.',
['b-icon-gear', 'b-icon-download'],
'15 minutes',
3
);
}
getStepDefinitions() {
return [
{ step: 1, name: 'Prepare Backup', description: 'Initialize backup process and verify system' },
{ step: 2, name: 'Export Data', description: 'Export inventory, settings, and user data' },
{ step: 3, name: 'Finalize Backup', description: 'Compress and store backup file' }
];
}
getInitialPayload() {
return {
...super.getInitialPayload(),
backup_options: {
include_inventory: true,
include_settings: true,
include_user_data: true,
include_files: false,
compression: true
}
};
}
}
/**
* Workflow Registry
* Central registry for all available workflow types
*/
class WorkflowRegistry {
constructor() {
this.workflows = new Map();
this.registerDefaultWorkflows();
}
/**
* Register all default workflow types
*/
registerDefaultWorkflows() {
this.register(new FotoFirstImportWorkflow());
this.register(new BulkItemImportWorkflow());
this.register(new InventoryAuditWorkflow());
this.register(new StorageOptimizationWorkflow());
this.register(new MaintenanceScheduleWorkflow());
this.register(new ExpiryCheckWorkflow());
this.register(new DataBackupWorkflow());
}
/**
* Register a workflow type
*/
register(workflow) {
if (!(workflow instanceof BaseWorkflow)) {
throw new Error('Workflow must extend BaseWorkflow');
...ownPayload
}
this.workflows.set(workflow.id, workflow);
}
/**
* Get a workflow by ID
*/
get(id) {
return this.workflows.get(id);
}
/**
* Get all registered workflows
*/
getAll() {
return Array.from(this.workflows.values());
}
/**
* Get workflows by category
*/
getByCategory(category) {
return this.getAll().filter(workflow => workflow.category === category);
}
/**
* Get all unique categories
*/
getCategories() {
return [...new Set(this.getAll().map(workflow => workflow.category))];
}
};
}
// Create and export the default registry instance
export const workflowRegistry = new WorkflowRegistry();
/**
* The backend stores WorkflowInstance.payload as an opaque string - it never
* parses or understands it as JSON. The frontend is fully responsible for
@ -413,7 +114,6 @@ export function serializeWorkflowPayload(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 = {};
@ -424,19 +124,13 @@ export function deserializeWorkflowPayload(workflow) {
}
return {...workflow, payload};
}
// Export individual classes for direct use
export {
BaseWorkflow,
InventoryAuditWorkflow,
StorageOptimizationWorkflow,
FotoFirstImportWorkflow,
MaintenanceScheduleWorkflow,
ExpiryCheckWorkflow,
DataBackupWorkflow,
BulkItemImportWorkflow,
WorkflowRegistry
export default {
getAllWorkflows,
getWorkflow,
getWorkflowComponent,
getWorkflowsByCategory,
getWorkflowCategories,
buildWorkflowApiPayload,
serializeWorkflowPayload,
deserializeWorkflowPayload
};
// Export default registry
export default workflowRegistry;