@@ -11,6 +11,7 @@ import {
1111 useState ,
1212} from 'react'
1313import {
14+ Connection ,
1415 Edge ,
1516 EdgeChange ,
1617 EdgeMouseHandler ,
@@ -24,7 +25,13 @@ import {
2425 useNodesState ,
2526} from 'reactflow'
2627import { useToggle } from 'usehooks-ts'
27- import { SOURCE_HANDLE_PROMPT_YES , useMapActionToLabel } from './constant'
28+ import {
29+ SOURCE_HANDLE_PROMPT_NO ,
30+ SOURCE_HANDLE_PROMPT_YES ,
31+ SOURCE_HANDLE_VARIABLES_NO ,
32+ SOURCE_HANDLE_VARIABLES_YES ,
33+ useMapActionToLabel ,
34+ } from './constant'
2835
2936type FlowCtx = {
3037 flow : TFlowInput
@@ -46,6 +53,8 @@ type FlowCtx = {
4653 handleDoubleClickEdge : EdgeMouseHandler
4754 getNode : ( id : string ) => Node < any > | undefined
4855 getEdge : ( id : string ) => Edge < any > | undefined
56+ handleValidateConnection : ( connection : Connection ) => boolean
57+ getCompleteFlows : ( ) => any [ ]
4958}
5059
5160const FlowContext = createContext < FlowCtx | undefined > ( undefined )
@@ -343,6 +352,120 @@ export const FlowProvider = ({ children, flow }: Props) => {
343352 [ _getNode ] ,
344353 )
345354
355+ /**
356+ * Checks the condition node to determine if it is valid.
357+ *
358+ * @param connection - The connection object.
359+ * @param sourceHandleNo - The source handle for the "No" path.
360+ * @param sourceHandleYes - The source handle for the "Yes" path.
361+ * @returns True if the condition node is valid, false otherwise.
362+ */
363+ const handleCheckConditionNode = useCallback (
364+ ( {
365+ connection,
366+ sourceHandleNo,
367+ sourceHandleYes,
368+ } : {
369+ connection : Connection
370+ sourceHandleYes : string
371+ sourceHandleNo : string
372+ } ) => {
373+ const numberOfYes = edges . filter ( ( edge ) => {
374+ return (
375+ edge . source === connection . source &&
376+ edge . sourceHandle === sourceHandleYes
377+ )
378+ } )
379+
380+ const numberOfNo = edges . filter ( ( edge ) => {
381+ return (
382+ edge . source === connection . source &&
383+ edge . sourceHandle === sourceHandleNo
384+ )
385+ } )
386+
387+ if (
388+ numberOfYes . length === 1 &&
389+ connection . sourceHandle === sourceHandleYes
390+ ) {
391+ return false
392+ }
393+
394+ if (
395+ numberOfNo . length === 1 &&
396+ connection . sourceHandle === sourceHandleNo
397+ ) {
398+ return false
399+ }
400+
401+ const targetEdge = edges . find (
402+ ( edge ) =>
403+ edge . target === connection . target &&
404+ edge . source === connection . source &&
405+ edge . sourceHandle !== null ,
406+ )
407+
408+ if ( targetEdge ) {
409+ return false
410+ }
411+
412+ return true
413+ } ,
414+ [ edges ] ,
415+ )
416+
417+ /**
418+ * Handles the validation of a connection.
419+ *
420+ * @param connection - The connection to validate.
421+ * @returns A boolean indicating whether the connection is valid or not.
422+ */
423+ const handleValidateConnection = useCallback (
424+ ( connection : Connection ) => {
425+ const sourcesFromHandleInState = edges . filter (
426+ ( edge ) => edge . source === connection . source ,
427+ ) . length
428+ const sourceNode = nodes . find ( ( node ) => node . id === connection . source )
429+
430+ const targetFromHandleInState = edges . filter (
431+ ( edge ) => edge . target === connection . target ,
432+ ) . length
433+
434+ if ( connection . source === connection . target ) return false
435+
436+ if (
437+ sourceNode ?. data . action === EActionTypes . START &&
438+ sourcesFromHandleInState < 2
439+ )
440+ return true
441+
442+ if ( sourceNode ?. data . action === EActionTypes . PROMPT_AND_COLLECT ) {
443+ return handleCheckConditionNode ( {
444+ connection,
445+ sourceHandleNo : SOURCE_HANDLE_PROMPT_NO ,
446+ sourceHandleYes : SOURCE_HANDLE_PROMPT_YES ,
447+ } )
448+ }
449+
450+ if (
451+ sourceNode ?. data . action === EActionTypes . CHECK_VARIABLES &&
452+ sourcesFromHandleInState < 2
453+ ) {
454+ return handleCheckConditionNode ( {
455+ connection,
456+ sourceHandleNo : SOURCE_HANDLE_VARIABLES_NO ,
457+ sourceHandleYes : SOURCE_HANDLE_VARIABLES_YES ,
458+ } )
459+ }
460+
461+ if ( targetFromHandleInState === 1 ) return false
462+ if ( sourcesFromHandleInState < 1 ) return true
463+
464+ return false
465+ } ,
466+ [ edges , nodes , handleCheckConditionNode ] ,
467+ )
468+
346469 /**
347470 * Handles the change of the selected node.
348471 *
@@ -389,6 +512,58 @@ export const FlowProvider = ({ children, flow }: Props) => {
389512 } )
390513 } , [ ] )
391514
515+ /**
516+ * Retrieves the complete flows from the given nodes.
517+ * A complete flow is a sequence of nodes where each node has a next action or is a final node.
518+ * Nodes that do not have a next action and are not final nodes are removed from the result.
519+ *
520+ * @returns An array of data objects representing the complete flows.
521+ */
522+ const getCompleteFlows = useCallback ( ( ) => {
523+ let clonedNodes = _ . cloneDeep ( nodes )
524+
525+ const nodeIsNextAction = ( node : Node < any > ) => {
526+ return clonedNodes . some ( ( nd ) => {
527+ return (
528+ nd . data ?. nextAction === node . id ||
529+ nd . data ?. nextActions ?. some ( ( na : any ) => na . id === node . id )
530+ )
531+ } )
532+ }
533+
534+ const nodeIsToBeRemoved = ( node : Node < any > ) => {
535+ if (
536+ node . data . action === EActionTypes . START ||
537+ node . data . action === EActionTypes . FALLBACK
538+ ) {
539+ return false
540+ }
541+
542+ if (
543+ ! nodeIsNextAction ( node ) &&
544+ ( ! node . data ?. nextAction || ! node . data ?. nextActions )
545+ ) {
546+ console . log ( node . id )
547+ return true
548+ }
549+
550+ return false
551+ }
552+
553+ // eslint-disable-next-line no-constant-condition
554+ while ( true ) {
555+ const nodeToBeRemoved = clonedNodes . find ( nodeIsToBeRemoved )
556+
557+ if ( ! nodeToBeRemoved ) {
558+ break
559+ }
560+
561+ clonedNodes = clonedNodes . filter ( ( node ) => node . id !== nodeToBeRemoved . id )
562+ }
563+
564+ return clonedNodes . map ( ( node ) => node . data )
565+ } , [ nodes ] )
566+
392567 useDidUpdate ( ( ) => {
393568 setNodes ( ( nds ) => {
394569 return nds . map ( ( node ) =>
@@ -419,6 +594,8 @@ export const FlowProvider = ({ children, flow }: Props) => {
419594 handleDoubleClickEdge,
420595 getNode : _getNode ,
421596 getEdge,
597+ handleValidateConnection,
598+ getCompleteFlows,
422599 } }
423600 >
424601 { children }
0 commit comments