1- import { IStep } from '@plumber/types'
1+ import { IStep , IStepApprovalBranch } from '@plumber/types'
22
3+ import { useCallback } from 'react'
34import { useMutation } from '@apollo/client'
45import { useToast } from '@opengovsg/design-system-react'
56
6- import { StepEnumType } from '@/graphql/__generated__/graphql'
7+ import {
8+ StepEnumType ,
9+ StepPositionInput ,
10+ } from '@/graphql/__generated__/graphql'
711import { UPDATE_STEP_POSITIONS } from '@/graphql/mutations/update-step-positions'
812import { GET_FLOW } from '@/graphql/queries/get-flow'
913
10- interface StepPositionInput {
11- id : string
12- position : number
13- type : StepEnumType
14- }
15-
1614const useReorderSteps = ( flowId : string ) => {
1715 const toast = useToast ( )
1816 const [ updateStepPositions ] = useMutation ( UPDATE_STEP_POSITIONS )
1917
18+ // TODO: write unit test for this function
19+ const calculateReorderedSteps = useCallback (
20+ ( {
21+ reorderedSteps,
22+ allSteps,
23+ mrfSteps,
24+ mrfApprovalSteps,
25+ approvalBranches,
26+ } : {
27+ reorderedSteps : IStep [ ]
28+ allSteps : IStep [ ]
29+ mrfSteps : IStep [ ]
30+ mrfApprovalSteps : IStep [ ]
31+ approvalBranches : { [ stepId : string ] : IStepApprovalBranch }
32+ } ) : StepPositionInput [ ] => {
33+ // we start from 2 because the first step is the trigger step and not part of the sortable list
34+ let nextPosition = 2
35+ let currentApprovalBranch : {
36+ stepId : string
37+ branch : 'approve' | 'reject'
38+ } | null = null
39+ const allReorderedSteps : StepPositionInput [ ] = [ ]
40+ reorderedSteps . forEach ( ( reorderingStep ) => {
41+ const isMrfStep = mrfSteps . some (
42+ ( mrfStep ) => mrfStep . id === reorderingStep . id ,
43+ )
44+
45+ if ( isMrfStep ) {
46+ // if the previous approval config is in the approve branch
47+ // we need to add the steps in the reject branch that was hidden
48+ if ( currentApprovalBranch ?. branch === 'approve' ) {
49+ const stepsInRejectBranch = allSteps . filter (
50+ ( step ) =>
51+ step . config ?. approval ?. branch === 'reject' &&
52+ step . config ?. approval ?. stepId === currentApprovalBranch ?. stepId ,
53+ ) as IStep [ ]
54+ stepsInRejectBranch . forEach ( ( step ) => {
55+ allReorderedSteps . push ( {
56+ id : step . id ,
57+ position : nextPosition ++ ,
58+ type : step . type as StepEnumType ,
59+ } )
60+ } )
61+ }
62+ currentApprovalBranch = null
63+ }
64+
65+ // add the current step to the list
66+ allReorderedSteps . push ( {
67+ id : reorderingStep . id ,
68+ position : nextPosition ++ ,
69+ type : reorderingStep . type as StepEnumType ,
70+ config :
71+ currentApprovalBranch ?. branch === 'reject'
72+ ? {
73+ approval : currentApprovalBranch ,
74+ }
75+ : { approval : null } , // this sets it to approval branch
76+ } )
77+
78+ const isMrfApprovalStep = mrfApprovalSteps . some (
79+ ( mrfApprovalStep ) => mrfApprovalStep . id === reorderingStep . id ,
80+ )
81+ if ( isMrfApprovalStep ) {
82+ currentApprovalBranch = {
83+ branch : approvalBranches [ reorderingStep . id ] ,
84+ stepId : reorderingStep . id ,
85+ }
86+ // if the current approval config is in the reject branch
87+ // we need to add the steps in the approve branch that was hidden
88+ if ( currentApprovalBranch ?. branch === 'reject' ) {
89+ const reorderingStepIndex = allSteps . findIndex (
90+ ( step ) => step . id === reorderingStep . id ,
91+ )
92+ const stepsInApproveBranch = [ ]
93+ // Search for non-mrf steps directly after the approval step
94+ for ( let i = reorderingStepIndex + 1 ; i < allSteps . length ; i ++ ) {
95+ const step = allSteps [ i ]
96+ if (
97+ ! step . config ?. approval &&
98+ ! mrfSteps . some ( ( mrfStep ) => mrfStep . id === step . id )
99+ ) {
100+ stepsInApproveBranch . push ( step )
101+ } else {
102+ break
103+ }
104+ }
105+ stepsInApproveBranch . forEach ( ( step ) => {
106+ allReorderedSteps . push ( {
107+ id : step . id ,
108+ position : nextPosition ++ ,
109+ type : step . type as StepEnumType ,
110+ } )
111+ } )
112+ }
113+ }
114+ } )
115+
116+ // all later steps not in the sortable list need not be updated since
117+ // reordering of visible steps will not affect them
118+
119+ return allReorderedSteps
120+ } ,
121+ [ ] ,
122+ )
123+
20124 const handleReorderUpdate = async ( stepPositions : StepPositionInput [ ] ) => {
21125 try {
22126 await updateStepPositions ( {
@@ -25,6 +129,7 @@ const useReorderSteps = (flowId: string) => {
25129 updateStepPositions : stepPositions . map ( ( sp ) => ( {
26130 id : sp . id ,
27131 position : sp . position ,
132+ config : sp . config ? { approval : sp . config . approval } : undefined ,
28133 __typename : 'Step' as const ,
29134 } ) ) ,
30135 } ,
@@ -37,15 +142,22 @@ const useReorderSteps = (flowId: string) => {
37142
38143 if ( flow ) {
39144 // Create a map of step positions for quick lookup
40- const positionMap = new Map (
41- stepPositions . map ( ( sp ) => [ sp . id , sp . position ] ) ,
145+ const updatedStepMap = new Map (
146+ stepPositions . map ( ( sp ) => [
147+ sp . id ,
148+ { position : sp . position , config : sp . config } ,
149+ ] ) ,
42150 )
43151
44152 // Update steps with new positions
45153 const updatedSteps = flow . steps . map ( ( step : IStep ) => {
46- const newPosition = positionMap . get ( step . id )
47- return newPosition !== undefined
48- ? { ...step , position : newPosition }
154+ const updatedStep = updatedStepMap . get ( step . id )
155+ return updatedStep !== undefined
156+ ? {
157+ ...step ,
158+ position : updatedStep . position ,
159+ config : { ...step . config , ...updatedStep . config } ,
160+ }
49161 : step
50162 } )
51163
@@ -91,7 +203,7 @@ const useReorderSteps = (flowId: string) => {
91203 }
92204 }
93205
94- return { handleReorderUpdate }
206+ return { handleReorderUpdate, calculateReorderedSteps }
95207}
96208
97209export default useReorderSteps
0 commit comments