Enhance CustomNode and WorkflowEditor components: Added useCallback for edit and delete handlers, improved button interactions, and ensured node selection state is managed correctly. Updated log file with new server and node registry initialization messages.
This commit is contained in:
parent
1c317d1a1d
commit
e9cb32eb0e
@ -200,3 +200,13 @@
|
||||
{"level":"debug","message":"Loaded node type: webhook","service":"flowforge-backend","timestamp":"2025-06-08 13:50:29"}
|
||||
{"level":"info","message":"Node registry initialized with 6 node types","service":"flowforge-backend","timestamp":"2025-06-08 13:50:29"}
|
||||
{"level":"info","message":"Database connection established successfully","service":"flowforge-backend","timestamp":"2025-06-08 13:50:29"}
|
||||
{"level":"info","message":"Server running on port 4000","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"info","message":"Initializing node registry","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"debug","message":"Loaded node type: delay","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"debug","message":"Loaded node type: email","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"debug","message":"Loaded node type: function","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"debug","message":"Loaded node type: http-request","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"debug","message":"Loaded node type: logger","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"debug","message":"Loaded node type: webhook","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"info","message":"Node registry initialized with 6 node types","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
{"level":"info","message":"Database connection established successfully","service":"flowforge-backend","timestamp":"2025-06-09 13:39:55"}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { memo } from 'react';
|
||||
import React, { memo, useCallback } from 'react';
|
||||
import { Handle, Position } from 'reactflow';
|
||||
|
||||
const getNodeColor = (type) => {
|
||||
@ -71,50 +71,71 @@ const CustomNode = ({ id, data, selected, onEdit, onDelete }) => {
|
||||
const nodeColor = getNodeColor(data.nodeType);
|
||||
const nodeIcon = getNodeIcon(data.nodeType);
|
||||
|
||||
const handleEdit = (e) => {
|
||||
e.stopPropagation();
|
||||
const handleEdit = useCallback((e) => {
|
||||
e?.stopPropagation?.();
|
||||
e?.preventDefault?.();
|
||||
if (onEdit) {
|
||||
onEdit(id);
|
||||
}
|
||||
};
|
||||
}, [id, onEdit]);
|
||||
|
||||
const handleDelete = (e) => {
|
||||
e.stopPropagation();
|
||||
const handleDelete = useCallback((e) => {
|
||||
e?.stopPropagation?.();
|
||||
e?.preventDefault?.();
|
||||
if (onDelete) {
|
||||
onDelete(id);
|
||||
}
|
||||
};
|
||||
}, [id, onDelete]);
|
||||
|
||||
return (
|
||||
<div
|
||||
className={`group relative ${selected ? 'ring-2 ring-primary-400' : ''} bg-white rounded-md shadow-md border-l-4 ${nodeColor} p-3 min-w-[180px] cursor-grab active:cursor-grabbing hover:shadow-lg transition-shadow`}
|
||||
className={`group relative ${selected ? 'ring-2 ring-primary-400' : ''} bg-white rounded-md shadow-md border-l-4 ${nodeColor} p-3 min-w-[180px] cursor-grab active:cursor-grabbing hover:shadow-lg transition-all duration-200`}
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
>
|
||||
{/* Action buttons - show on hover or when selected */}
|
||||
<div className={`absolute -top-2 -right-2 flex space-x-1 transition-opacity ${selected ? 'opacity-100' : 'opacity-0 group-hover:opacity-100'}`}>
|
||||
{/* Action buttons - always visible for debugging */}
|
||||
<div
|
||||
className="absolute -top-2 -right-2 flex space-x-1 transition-all duration-200 z-50 opacity-100"
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
>
|
||||
{/* Edit button */}
|
||||
<button
|
||||
onClick={handleEdit}
|
||||
className="w-6 h-6 bg-blue-500 hover:bg-blue-600 text-white rounded-full flex items-center justify-center shadow-md transition-colors"
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
<div
|
||||
onMouseDown={handleEdit}
|
||||
className="w-10 h-10 bg-blue-500 hover:bg-blue-600 text-white rounded-full flex items-center justify-center shadow-xl transition-all duration-200 hover:scale-105 border-2 border-white cursor-pointer select-none"
|
||||
style={{
|
||||
pointerEvents: 'auto',
|
||||
userSelect: 'none',
|
||||
position: 'relative',
|
||||
zIndex: 1000,
|
||||
touchAction: 'manipulation'
|
||||
}}
|
||||
title="Edit node"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 pointer-events-none" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M11 5H6a2 2 0 00-2 2v11a2 2 0 002 2h11a2 2 0 002-2v-5m-1.414-9.414a2 2 0 112.828 2.828L11.828 15H9v-2.828l8.586-8.586z" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Delete button */}
|
||||
<button
|
||||
onClick={handleDelete}
|
||||
className="w-6 h-6 bg-red-500 hover:bg-red-600 text-white rounded-full flex items-center justify-center shadow-md transition-colors"
|
||||
style={{ pointerEvents: 'auto' }}
|
||||
<div
|
||||
onMouseDown={handleDelete}
|
||||
className="w-10 h-10 bg-red-500 hover:bg-red-600 text-white rounded-full flex items-center justify-center shadow-xl transition-all duration-200 hover:scale-105 border-2 border-white cursor-pointer select-none"
|
||||
style={{
|
||||
pointerEvents: 'auto',
|
||||
userSelect: 'none',
|
||||
position: 'relative',
|
||||
zIndex: 1000,
|
||||
touchAction: 'manipulation'
|
||||
}}
|
||||
title="Delete node"
|
||||
role="button"
|
||||
tabIndex={0}
|
||||
>
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-3 w-3" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" className="h-5 w-5 pointer-events-none" fill="none" viewBox="0 0 24 24" stroke="currentColor">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 7l-.867 12.142A2 2 0 0116.138 21H7.862a2 2 0 01-1.995-1.858L5 7m5 4v6m4-6v6m1-10V4a1 1 0 00-1-1h-4a1 1 0 00-1 1v3M4 7h16" />
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Input handle */}
|
||||
|
@ -1,4 +1,4 @@
|
||||
import React, { useState, useEffect, useCallback, useRef } from 'react';
|
||||
import React, { useState, useEffect, useCallback, useRef, useMemo } from 'react';
|
||||
import { useParams, useNavigate } from 'react-router-dom';
|
||||
import ReactFlow, {
|
||||
ReactFlowProvider,
|
||||
@ -51,16 +51,39 @@ const WorkflowEditor = () => {
|
||||
const [viewport, setViewport] = useState({ x: 0, y: 0, zoom: 1 });
|
||||
const [isFirstLoad, setIsFirstLoad] = useState(true);
|
||||
|
||||
// Handle node editing
|
||||
const handleEditNode = useCallback((nodeId) => {
|
||||
const node = nodes.find(n => n.id === nodeId);
|
||||
if (node) {
|
||||
setSelectedNode(node);
|
||||
}
|
||||
}, [nodes, setSelectedNode]);
|
||||
|
||||
// Handle node deletion
|
||||
const handleDeleteNode = useCallback((nodeId) => {
|
||||
// Show confirmation dialog
|
||||
if (window.confirm('Are you sure you want to delete this node?')) {
|
||||
setNodes((nds) => nds.filter((n) => n.id !== nodeId));
|
||||
setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId));
|
||||
|
||||
// Clear selection if the deleted node was selected
|
||||
if (selectedNode && selectedNode.id === nodeId) {
|
||||
setSelectedNode(null);
|
||||
}
|
||||
}
|
||||
}, [setNodes, setEdges, selectedNode, nodes]);
|
||||
|
||||
// Node type definitions for React Flow
|
||||
const nodeTypeDefinitions = {
|
||||
const nodeTypeDefinitions = useMemo(() => ({
|
||||
customNode: (nodeProps) => (
|
||||
<CustomNode
|
||||
{...nodeProps}
|
||||
selected={selectedNode && selectedNode.id === nodeProps.id}
|
||||
onEdit={handleEditNode}
|
||||
onDelete={handleDeleteNode}
|
||||
/>
|
||||
)
|
||||
};
|
||||
}), [selectedNode, handleEditNode, handleDeleteNode]);
|
||||
|
||||
// Load workflow if editing existing one
|
||||
useEffect(() => {
|
||||
@ -306,28 +329,6 @@ const WorkflowEditor = () => {
|
||||
setNodes((nds) => nds.concat(newNode));
|
||||
};
|
||||
|
||||
// Handle node editing
|
||||
const handleEditNode = useCallback((nodeId) => {
|
||||
const node = nodes.find(n => n.id === nodeId);
|
||||
if (node) {
|
||||
setSelectedNode(node);
|
||||
}
|
||||
}, [nodes]);
|
||||
|
||||
// Handle node deletion
|
||||
const handleDeleteNode = useCallback((nodeId) => {
|
||||
// Show confirmation dialog
|
||||
if (window.confirm('Are you sure you want to delete this node?')) {
|
||||
setNodes((nds) => nds.filter((n) => n.id !== nodeId));
|
||||
setEdges((eds) => eds.filter((e) => e.source !== nodeId && e.target !== nodeId));
|
||||
|
||||
// Clear selection if the deleted node was selected
|
||||
if (selectedNode && selectedNode.id === nodeId) {
|
||||
setSelectedNode(null);
|
||||
}
|
||||
}
|
||||
}, [setNodes, setEdges, selectedNode]);
|
||||
|
||||
// Handle workflow execution
|
||||
const handleExecuteWorkflow = (executionId) => {
|
||||
setLatestExecutionId(executionId);
|
||||
|
Loading…
x
Reference in New Issue
Block a user