stash
This commit is contained in:
parent
cfcc2c15d3
commit
4f2fe011c0
28 changed files with 3499 additions and 2922 deletions
|
|
@ -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;
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue