2026-04-04 17:56:00 +02:00
|
|
|
import { Handle, Position } from 'reactflow'
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* JoinNode - Pfad-Konsolidierung
|
|
|
|
|
*
|
|
|
|
|
* Properties:
|
|
|
|
|
* - data.label: Node-Label
|
|
|
|
|
* - data.join_strategy: 'wait_all' | 'wait_any' | 'best_effort'
|
|
|
|
|
* - data.skip_handling: 'ignore_skipped' | 'use_placeholder' | 'require_minimum'
|
|
|
|
|
* - selected: Boolean
|
|
|
|
|
*/
|
|
|
|
|
export function JoinNode({ data, selected }) {
|
|
|
|
|
const strategy = data.join_strategy || 'wait_all'
|
|
|
|
|
const skipHandling = data.skip_handling || 'ignore_skipped'
|
|
|
|
|
|
|
|
|
|
// Strategy Display Names
|
|
|
|
|
const strategyLabels = {
|
|
|
|
|
'wait_all': 'Alle warten',
|
|
|
|
|
'wait_any': 'Beliebig',
|
|
|
|
|
'best_effort': 'Best Effort'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const skipLabels = {
|
|
|
|
|
'ignore_skipped': 'Ignorieren',
|
|
|
|
|
'use_placeholder': 'Platzhalter',
|
|
|
|
|
'require_minimum': 'Minimum'
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={`workflow-node join-node ${selected ? 'selected' : ''}`}>
|
|
|
|
|
<div className="node-header">
|
|
|
|
|
<div className="node-icon">🔀</div>
|
|
|
|
|
<div className="node-label">{data.label || 'Join'}</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="node-body">
|
|
|
|
|
<div className="strategy">
|
|
|
|
|
<strong>Strategie:</strong> {strategyLabels[strategy] || strategy}
|
|
|
|
|
</div>
|
|
|
|
|
<div className="skip-handling">
|
|
|
|
|
<strong>Skip:</strong> {skipLabels[skipHandling] || skipHandling}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
2026-04-11 10:25:28 +02:00
|
|
|
{/* Mehrere Target Handles für eingehende Pfade (bis zu 8) */}
|
2026-04-04 17:56:00 +02:00
|
|
|
<Handle
|
|
|
|
|
type="target"
|
|
|
|
|
position={Position.Top}
|
|
|
|
|
id="path_1"
|
2026-04-11 10:25:28 +02:00
|
|
|
style={{ left: '11%', background: '#17A2B8' }}
|
2026-04-04 17:56:00 +02:00
|
|
|
/>
|
|
|
|
|
<Handle
|
|
|
|
|
type="target"
|
|
|
|
|
position={Position.Top}
|
|
|
|
|
id="path_2"
|
2026-04-11 10:25:28 +02:00
|
|
|
style={{ left: '22%', background: '#17A2B8' }}
|
2026-04-04 17:56:00 +02:00
|
|
|
/>
|
|
|
|
|
<Handle
|
|
|
|
|
type="target"
|
|
|
|
|
position={Position.Top}
|
|
|
|
|
id="path_3"
|
2026-04-11 10:25:28 +02:00
|
|
|
style={{ left: '33%', background: '#17A2B8' }}
|
|
|
|
|
/>
|
|
|
|
|
<Handle
|
|
|
|
|
type="target"
|
|
|
|
|
position={Position.Top}
|
|
|
|
|
id="path_4"
|
|
|
|
|
style={{ left: '44%', background: '#17A2B8' }}
|
|
|
|
|
/>
|
|
|
|
|
<Handle
|
|
|
|
|
type="target"
|
|
|
|
|
position={Position.Top}
|
|
|
|
|
id="path_5"
|
|
|
|
|
style={{ left: '56%', background: '#17A2B8' }}
|
|
|
|
|
/>
|
|
|
|
|
<Handle
|
|
|
|
|
type="target"
|
|
|
|
|
position={Position.Top}
|
|
|
|
|
id="path_6"
|
|
|
|
|
style={{ left: '67%', background: '#17A2B8' }}
|
|
|
|
|
/>
|
|
|
|
|
<Handle
|
|
|
|
|
type="target"
|
|
|
|
|
position={Position.Top}
|
|
|
|
|
id="path_7"
|
|
|
|
|
style={{ left: '78%', background: '#17A2B8' }}
|
|
|
|
|
/>
|
|
|
|
|
<Handle
|
|
|
|
|
type="target"
|
|
|
|
|
position={Position.Top}
|
|
|
|
|
id="path_8"
|
|
|
|
|
style={{ left: '89%', background: '#17A2B8' }}
|
2026-04-04 17:56:00 +02:00
|
|
|
/>
|
|
|
|
|
|
|
|
|
|
{/* Ein Source Handle für konsolidierten Ausgang */}
|
|
|
|
|
<Handle
|
|
|
|
|
type="source"
|
|
|
|
|
position={Position.Bottom}
|
|
|
|
|
id="out"
|
|
|
|
|
style={{ background: '#17A2B8' }}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|