1- import { IStep } from '@plumber/types'
1+ import { IStep , IStepApprovalBranch } from '@plumber/types'
22
3- import { useContext } from 'react'
3+ import { useCallback , useContext } from 'react'
44import { useMutation } from '@apollo/client'
55
66import { EditorContext } from '@/contexts/Editor'
7- import { StepEnumType } from '@/graphql/__generated__/graphql'
7+ import {
8+ StepEnumType ,
9+ StepPositionInput ,
10+ } from '@/graphql/__generated__/graphql'
811import { UPDATE_STEP_POSITIONS } from '@/graphql/mutations/update-step-positions'
912import { GET_FLOW } from '@/graphql/queries/get-flow'
1013
11- interface StepPositionInput {
12- id : string
13- position : number
14- type : StepEnumType
15- }
16-
1714const useReorderSteps = ( flowId : string ) => {
1815 const { flow } = useContext ( EditorContext )
1916 const [ updateStepPositions ] = useMutation ( UPDATE_STEP_POSITIONS , {
2017 refetchQueries : [ GET_FLOW ] ,
2118 } )
2219
20+ // TODO: write unit test for this function
21+ const calculateReorderedSteps = useCallback (
22+ ( {
23+ reorderedSteps,
24+ allSteps,
25+ mrfSteps,
26+ mrfApprovalSteps,
27+ approvalBranches,
28+ } : {
29+ reorderedSteps : IStep [ ]
30+ allSteps : IStep [ ]
31+ mrfSteps : IStep [ ]
32+ mrfApprovalSteps : IStep [ ]
33+ approvalBranches : { [ stepId : string ] : IStepApprovalBranch }
34+ } ) : StepPositionInput [ ] => {
35+ // we start from 2 because the first step is the trigger step and not part of the sortable list
36+ let nextPosition = 2
37+ let currentApprovalBranch : {
38+ stepId : string
39+ branch : 'approve' | 'reject'
40+ } | null = null
41+ const allReorderedSteps : StepPositionInput [ ] = [ ]
42+ reorderedSteps . forEach ( ( reorderingStep ) => {
43+ const isMrfStep = mrfSteps . some (
44+ ( mrfStep ) => mrfStep . id === reorderingStep . id ,
45+ )
46+
47+ if ( isMrfStep ) {
48+ // if the previous approval config is in the approve branch
49+ // we need to add the steps in the reject branch that was hidden
50+ if ( currentApprovalBranch ?. branch === 'approve' ) {
51+ const stepsInRejectBranch = allSteps . filter (
52+ ( step ) =>
53+ step . config ?. approval ?. branch === 'reject' &&
54+ step . config ?. approval ?. stepId === currentApprovalBranch ?. stepId ,
55+ ) as IStep [ ]
56+ stepsInRejectBranch . forEach ( ( step ) => {
57+ allReorderedSteps . push ( {
58+ id : step . id ,
59+ position : nextPosition ++ ,
60+ type : step . type as StepEnumType ,
61+ } )
62+ } )
63+ }
64+ currentApprovalBranch = null
65+ }
66+
67+ // add the current step to the list
68+ allReorderedSteps . push ( {
69+ id : reorderingStep . id ,
70+ position : nextPosition ++ ,
71+ type : reorderingStep . type as StepEnumType ,
72+ config :
73+ currentApprovalBranch ?. branch === 'reject'
74+ ? {
75+ approval : currentApprovalBranch ,
76+ }
77+ : { approval : null } , // this sets it to approval branch
78+ } )
79+
80+ const isMrfApprovalStep = mrfApprovalSteps . some (
81+ ( mrfApprovalStep ) => mrfApprovalStep . id === reorderingStep . id ,
82+ )
83+ if ( isMrfApprovalStep ) {
84+ currentApprovalBranch = {
85+ branch : approvalBranches [ reorderingStep . id ] ,
86+ stepId : reorderingStep . id ,
87+ }
88+ // if the current approval config is in the reject branch
89+ // we need to add the steps in the approve branch that was hidden
90+ if ( currentApprovalBranch ?. branch === 'reject' ) {
91+ const reorderingStepIndex = allSteps . findIndex (
92+ ( step ) => step . id === reorderingStep . id ,
93+ )
94+ const stepsInApproveBranch = [ ]
95+ // Search for non-mrf steps directly after the approval step
96+ for ( let i = reorderingStepIndex + 1 ; i < allSteps . length ; i ++ ) {
97+ const step = allSteps [ i ]
98+ if (
99+ ! step . config ?. approval &&
100+ ! mrfSteps . some ( ( mrfStep ) => mrfStep . id === step . id )
101+ ) {
102+ stepsInApproveBranch . push ( step )
103+ } else {
104+ break
105+ }
106+ }
107+ stepsInApproveBranch . forEach ( ( step ) => {
108+ allReorderedSteps . push ( {
109+ id : step . id ,
110+ position : nextPosition ++ ,
111+ type : step . type as StepEnumType ,
112+ } )
113+ } )
114+ }
115+ }
116+ } )
117+
118+ // all later steps not in the sortable list need not be updated since
119+ // reordering of visible steps will not affect them
120+
121+ return allReorderedSteps
122+ } ,
123+ [ ] ,
124+ )
125+
23126 const handleReorderUpdate = async ( stepPositions : StepPositionInput [ ] ) => {
24127 try {
25128 await updateStepPositions ( {
@@ -32,6 +135,7 @@ const useReorderSteps = (flowId: string) => {
32135 steps : stepPositions . map ( ( sp ) => ( {
33136 id : sp . id ,
34137 position : sp . position ,
138+ config : sp . config ? { approval : sp . config . approval } : undefined ,
35139 __typename : 'Step' as const ,
36140 } ) ) ,
37141 } ,
@@ -45,15 +149,22 @@ const useReorderSteps = (flowId: string) => {
45149
46150 if ( flow ) {
47151 // Create a map of step positions for quick lookup
48- const positionMap = new Map (
49- stepPositions . map ( ( sp ) => [ sp . id , sp . position ] ) ,
152+ const updatedStepMap = new Map (
153+ stepPositions . map ( ( sp ) => [
154+ sp . id ,
155+ { position : sp . position , config : sp . config } ,
156+ ] ) ,
50157 )
51158
52159 // Update steps with new positions
53160 const updatedSteps = flow . steps . map ( ( step : IStep ) => {
54- const newPosition = positionMap . get ( step . id )
55- return newPosition !== undefined
56- ? { ...step , position : newPosition }
161+ const updatedStep = updatedStepMap . get ( step . id )
162+ return updatedStep !== undefined
163+ ? {
164+ ...step ,
165+ position : updatedStep . position ,
166+ config : { ...step . config , ...updatedStep . config } ,
167+ }
57168 : step
58169 } )
59170
@@ -84,7 +195,7 @@ const useReorderSteps = (flowId: string) => {
84195 }
85196 }
86197
87- return { handleReorderUpdate }
198+ return { handleReorderUpdate, calculateReorderedSteps }
88199}
89200
90201export default useReorderSteps
0 commit comments