Visual workflow builder and canvas component for Filament applications
npm install @hadyfayed/filament-workflow-canvasA visual workflow builder and canvas component for Laravel applications. Built on top of the React Wrapper package, providing a drag-and-drop interface for creating complex workflows.
- 🎨 Visual Workflow Builder - Drag-and-drop interface for building workflows
- 🔗 Node-based System - Trigger, Condition, Transform, and Analytics nodes
- 📊 Real-time Preview - Live workflow execution preview
- 💾 Auto-save - Automatic saving of workflow changes
- 🔄 State Management - Advanced state management with persistence
- 🎯 Type Safe - Full TypeScript support
- 🖥️ Fullscreen Mode - Expandable canvas for complex workflows
- 🔍 Validation - Built-in workflow validation and error detection
- 📱 Responsive - Works on desktop and tablet devices
``bash`
composer require hadyfayed/filament-workflow-canvas
npm install @hadyfayed/filament-workflow-canvas
Dependencies:
This package requires the React Wrapper as a base:
`bash`
composer require hadyfayed/filament-react-wrapper
npm install @hadyfayed/filament-react-wrapper
`bash`Copy package files to your resources directory
cp -r packages/react-wrapper/resources/js/* resources/js/react-wrapper/
cp -r packages/workflow-canvas/resources/js/* resources/js/workflow-canvas/
Publish the configuration files:
`bash`
php artisan vendor:publish --tag=workflow-canvas-config
Publish the assets:
`bash`
php artisan vendor:publish --tag=workflow-canvas-assets
Run the migrations:
`bash`
php artisan migrate
`javascript
// vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [
laravel({
input: [
'resources/css/app.css',
'resources/js/app.js',
'resources/js/bootstrap-react.tsx'
],
refresh: true,
}),
react(),
],
resolve: {
alias: {
'@': '/resources/js',
},
},
});
`
For distributed packages:
`tsx
// resources/js/bootstrap-react.tsx
import React from 'react';
// Initialize the React wrapper core system
import '@hadyfayed/filament-react-wrapper/bootstrap';
// Initialize workflow canvas components
import '@hadyfayed/filament-workflow-canvas/bootstrap';
console.log('React Wrapper and Workflow Canvas bootstrapped for Filament integration');
export default function bootstrap() {
return true;
}
`
For local development:
`tsx
// resources/js/bootstrap-react.tsx
import React from 'react';
// Initialize the React wrapper core system
import './react-wrapper/core';
// Initialize workflow canvas components
import './workflow-canvas/index';
console.log('React Wrapper and Workflow Canvas bootstrapped for Filament integration');
export default function bootstrap() {
return true;
}
`
`blade`
:readonly="false"
height="600px"
state-path="workflow.canvas_data"
/>
`php
use HadyFayed\WorkflowCanvas\Models\Workflow;
class WorkflowBuilder extends Component
{
public Workflow $workflow;
public array $canvasData = [];
public function mount(Workflow $workflow)
{
$this->workflow = $workflow;
$this->canvasData = $workflow->canvas_data ?? [];
}
public function updatedCanvasData()
{
$this->workflow->update(['canvas_data' => $this->canvasData]);
}
public function render()
{
return view('livewire.workflow-builder');
}
}
`
`blade`
state-path="canvasData"
/>
#### Form Field Integration
`php
use HadyFayed\WorkflowCanvas\Forms\Components\WorkflowCanvasField;
// In your Filament Resource
WorkflowCanvasField::make('canvas_data')
->reactive() // Enable real-time React-PHP state sync
->lazy() // Load component when visible (performance optimization)
->enableAutoSave() // Auto-save workflow changes
->showMinimap() // Show workflow minimap
->enableFullscreen() // Allow fullscreen editing
->nodeTypes(config('workflow-canvas.node_types'))
->onWorkflowChange(fn($state) => $this->processWorkflow($state));
`
#### Widget Integration
`php
use HadyFayed\WorkflowCanvas\Widgets\WorkflowStatsWidget;
// In your Filament Panel
class WorkflowStatsWidget extends ReactWidget
{
protected string $componentName = 'WorkflowStatsWidget';
public function getData(): array
{
return $this->getWorkflowStats(); // Data automatically shared with React
}
}
`
#### No Plugin Required!
React Wrapper v3.0 provides direct Filament integration without requiring plugin registration:
- Components work directly with Filament forms and resources
- Lazy loading and asset management handled automatically
- 90%+ React-PHP function mapping for seamless integration
The package comes with four built-in node types:
Customize the workflow canvas in config/workflow-canvas.php:
`php`
return [
'canvas' => [
'default_height' => '600px',
'fullscreen_enabled' => true,
'auto_save' => true,
'auto_save_delay' => 500,
],
'node_types' => [
'trigger' => [
'label' => 'Trigger',
'icon' => '⚡',
'color' => 'blue',
'processor' => TriggerProcessor::class,
],
// ... more node types
],
'validation' => [
'max_nodes' => 100,
'max_connections' => 200,
'required_trigger' => true,
'prevent_cycles' => true,
],
];
Create custom node types by extending the base processor:
`php
use HadyFayed\WorkflowCanvas\Processors\BaseNodeProcessor;
class CustomProcessor extends BaseNodeProcessor
{
public function process(mixed $node, array $inputData, WorkflowExecution $execution): array
{
// Your custom processing logic
return $outputData;
}
public function getConfigSchema(): array
{
return [
'custom_field' => [
'type' => 'text',
'label' => 'Custom Field',
'required' => true,
],
];
}
}
`
Register the custom node type:
`php`
// config/workflow-canvas.php
'node_types' => [
'custom' => [
'label' => 'Custom Node',
'icon' => '⭐',
'color' => 'purple',
'processor' => CustomProcessor::class,
],
],
Execute workflows programmatically:
`php
use HadyFayed\WorkflowCanvas\Services\WorkflowExecutionService;
$executionService = app(WorkflowExecutionService::class);
$result = $executionService->execute($workflow, $inputData);
if ($result->isSuccessful()) {
$outputData = $result->getOutputData();
} else {
$errors = $result->getErrors();
}
`
The package dispatches several events during workflow execution:
`php
use HadyFayed\WorkflowCanvas\Events\WorkflowStarted;
use HadyFayed\WorkflowCanvas\Events\WorkflowCompleted;
use HadyFayed\WorkflowCanvas\Events\WorkflowFailed;
// Listen for workflow events
Event::listen(WorkflowStarted::class, function ($event) {
Log::info('Workflow started', ['workflow_id' => $event->workflow->id]);
});
`
`php
$workflow = Workflow::create([
'name' => 'My Workflow',
'description' => 'A sample workflow',
'canvas_data' => $canvasData,
'is_active' => true,
]);
// Validate workflow structure
$errors = $workflow->validate();
// Duplicate workflow
$copy = $workflow->duplicate('New Name');
// Check for cycles
$hasCycles = $workflow->hasCycles();
`
`php`
WorkflowCanvasComponent::make(
workflow: $workflow,
readonly: false,
height: '800px',
statePath: 'canvas_data'
)
When developing the packages locally:
`bashIn the package directory
cd packages/workflow-canvas
npm install
npm run build # Build distributable package
$3
`bash
Check types in package
cd packages/workflow-canvas
npm run typecheckBuild with type declarations
npm run build
`$3
`bash
Start development server
npm run devBuild for production
npm run build
`$3
`bash
Package tests
cd packages/workflow-canvas
composer test
npm testMain app tests
cd ../../
php artisan test
`$3
Create custom nodes by extending the base components:
`tsx
// resources/js/components/CustomNode.tsx
import React from 'react';
import { NodeProps } from 'reactflow';export default function CustomNode({ data }: NodeProps) {
return (
{data.label}
{/ Your custom UI /}
);
}
`Register the custom node:
`tsx
// resources/js/bootstrap-react.tsx
import { componentRegistry } from './react-wrapper/core';
import CustomNode from './components/CustomNode';componentRegistry.register({
name: 'CustomNode',
component: CustomNode,
metadata: {
category: 'custom',
description: 'Custom workflow node'
}
});
``MIT License. See LICENSE for details.