Skip to content

Commit a515705

Browse files
committed
refactor(frontend): refactor contexts
1 parent aef16f2 commit a515705

File tree

14 files changed

+237
-205
lines changed

14 files changed

+237
-205
lines changed

packages/frontend/src/components/Editor/AddStepButton.tsx renamed to packages/frontend/src/components/Editor/components/AddStepButton.tsx

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,11 +4,10 @@ import { BiPlus } from 'react-icons/bi'
44
import { Box, Divider, useDisclosure } from '@chakra-ui/react'
55
import { IconButton, TouchableTooltip } from '@opengovsg/design-system-react'
66

7+
import EmptyFlowStepHeader from '@/components/EmptyFlowStepHeader'
8+
import FlowStepConfigurationModal from '@/components/FlowStepConfigurationModal'
79
import { useUnsavedChanges } from '@/hooks/useUnsavedChanges'
810

9-
import EmptyFlowStepHeader from '../EmptyFlowStepHeader'
10-
import FlowStepConfigurationModal from '../FlowStepConfigurationModal'
11-
1211
import UnsavedChangesAlert from './UnsavedChangesAlert'
1312

1413
interface AddStepButtonProps {

packages/frontend/src/components/Editor/FlowStepWithAddButton.tsx renamed to packages/frontend/src/components/Editor/components/FlowStepWithAddButton.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ import { IStep } from '@plumber/types'
33
import { FlowStep } from '@/exports/components'
44
import { useStepMetadata } from '@/hooks/useStepMetadata'
55

6-
import { ApproveReject } from '../FlowStep/components/ApproveReject'
6+
import { ApproveReject } from '../../FlowStep/components/ApproveReject'
77

88
import { AddStepButton } from './AddStepButton'
99

Lines changed: 146 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,146 @@
1+
import { IStep } from '@plumber/types'
2+
3+
import { useContext } from 'react'
4+
import { Center, Flex } from '@chakra-ui/react'
5+
6+
import PrimarySpinner from '@/components/PrimarySpinner'
7+
import { SortableList } from '@/components/SortableList'
8+
import { EditorContext } from '@/contexts/Editor'
9+
import { StepExecutionsToIncludeContext } from '@/contexts/StepExecutionsToInclude'
10+
import { FlowStepGroup } from '@/exports/components'
11+
import { StepEnumType } from '@/graphql/__generated__/graphql'
12+
import { TOOLBOX_ACTIONS } from '@/helpers/toolbox'
13+
import useReorderSteps from '@/hooks/useReorderSteps'
14+
15+
import { EDITOR_RIGHT_DRAWER_WIDTH } from '../constants'
16+
import { editorStyles } from '../styles'
17+
18+
import FlowStepWithAddButton from './FlowStepWithAddButton'
19+
20+
interface StepsListProps {
21+
isNested?: boolean
22+
}
23+
24+
export function StepsList({ isNested }: StepsListProps) {
25+
const {
26+
triggerStep,
27+
actionStepsBeforeGroup,
28+
groupedSteps,
29+
appsWithActions,
30+
groupingActions,
31+
} = useContext(StepExecutionsToIncludeContext)
32+
const { flow, isDrawerOpen, isMobile, readOnly } = useContext(EditorContext)
33+
34+
const { handleReorderUpdate } = useReorderSteps(flow.id)
35+
36+
const handleReorderSteps = async (reorderedSteps: IStep[]) => {
37+
const stepPositions = reorderedSteps.map((step, index) => ({
38+
id: step.id,
39+
position: index + 2, // trigger position is 1
40+
type: step.type as StepEnumType,
41+
}))
42+
43+
try {
44+
await handleReorderUpdate(stepPositions)
45+
} catch (error) {
46+
console.error(
47+
'Error updating step positions: ',
48+
error,
49+
JSON.stringify(stepPositions),
50+
)
51+
}
52+
}
53+
54+
const nonIfThenActionSteps = actionStepsBeforeGroup.filter(
55+
(step) => step.key !== TOOLBOX_ACTIONS.IfThen,
56+
)
57+
58+
// Disables last add step and hide in-between add step buttons
59+
const hasExactlyOneEmptyActionStep =
60+
nonIfThenActionSteps.length === 1 && !nonIfThenActionSteps[0].appKey
61+
62+
// Disables last add step button but show empty action instead
63+
const hasNoActionSteps = nonIfThenActionSteps.length === 0
64+
const shouldShowEmptyAction = hasNoActionSteps && !groupedSteps.length
65+
// for backwards compatibility where empty step is created
66+
const shouldDisableAddButton =
67+
(hasExactlyOneEmptyActionStep || hasNoActionSteps) && !groupedSteps.length
68+
69+
if (!appsWithActions || !groupingActions) {
70+
return (
71+
<Center height="100vh" position="fixed" width="full" top={0} left={0}>
72+
<PrimarySpinner fontSize="4xl" />
73+
</Center>
74+
)
75+
}
76+
const leftStepPadding = isDrawerOpen ? (isMobile ? 0 : '5rem') : 0
77+
78+
return (
79+
<Flex
80+
{...editorStyles.stepHeaderContainer}
81+
flex={isDrawerOpen ? (isMobile ? 0 : 1) : undefined}
82+
px={leftStepPadding}
83+
maxWidth={`calc(100% - ${
84+
isDrawerOpen ? EDITOR_RIGHT_DRAWER_WIDTH : '0px'
85+
})`}
86+
>
87+
{triggerStep && (
88+
<FlowStepWithAddButton
89+
step={triggerStep}
90+
isLastStep={
91+
actionStepsBeforeGroup.length === 0 && groupedSteps.length === 0
92+
}
93+
isNested={isNested}
94+
stepsBeforeGroup={[]} // no reason to pass in for this
95+
groupedSteps={groupedSteps}
96+
addButtonProps={{
97+
isHidden: readOnly,
98+
isDisabled: shouldDisableAddButton,
99+
showEmptyAction: shouldShowEmptyAction,
100+
}}
101+
/>
102+
)}
103+
104+
<SortableList
105+
items={actionStepsBeforeGroup}
106+
onChange={handleReorderSteps}
107+
renderItem={(step, isOverlay) => {
108+
const { id, position } = step
109+
return (
110+
<SortableList.Item id={id}>
111+
<Flex
112+
key={`${id}-${position}`}
113+
width={isDrawerOpen || isMobile ? '100%' : 'auto'}
114+
flexDir="column"
115+
position="relative"
116+
>
117+
<FlowStepWithAddButton
118+
step={step}
119+
isLastStep={
120+
groupedSteps.length === 0 &&
121+
actionStepsBeforeGroup[actionStepsBeforeGroup.length - 1]
122+
.id === step.id
123+
}
124+
isNested={isNested}
125+
stepsBeforeGroup={actionStepsBeforeGroup}
126+
groupedSteps={groupedSteps}
127+
addButtonProps={{
128+
isHidden: readOnly || !!isOverlay,
129+
isDisabled: shouldDisableAddButton,
130+
showEmptyAction: shouldShowEmptyAction,
131+
}}
132+
/>
133+
</Flex>
134+
</SortableList.Item>
135+
)
136+
}}
137+
/>
138+
{groupedSteps.length > 0 && (
139+
<FlowStepGroup
140+
stepsBeforeGroup={actionStepsBeforeGroup}
141+
groupedSteps={groupedSteps}
142+
/>
143+
)}
144+
</Flex>
145+
)
146+
}

packages/frontend/src/components/Editor/index.tsx

Lines changed: 9 additions & 179 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,13 @@
1-
import type { IApp, IStep } from '@plumber/types'
2-
31
import { useContext, useMemo } from 'react'
4-
import { Center, Flex } from '@chakra-ui/react'
2+
import { Flex } from '@chakra-ui/react'
53

64
import EditorRightDrawer from '@/components/EditorRightDrawer'
7-
import FlowStepGroup from '@/components/FlowStepGroup'
8-
import { SortableList } from '@/components/SortableList'
95
import { EditorContext } from '@/contexts/Editor'
106
import { MrfContextProvider } from '@/contexts/MrfContext'
117
import { StepExecutionsToIncludeProvider } from '@/contexts/StepExecutionsToInclude'
12-
import { StepEnumType } from '@/graphql/__generated__/graphql'
13-
import { extractBranchesWithSteps, TOOLBOX_ACTIONS } from '@/helpers/toolbox'
14-
import useReorderSteps from '@/hooks/useReorderSteps'
15-
16-
import PrimarySpinner from '../PrimarySpinner'
178

9+
import { StepsList } from './components/StepsList'
1810
import { EDITOR_RIGHT_DRAWER_WIDTH } from './constants'
19-
import FlowStepWithAddButton from './FlowStepWithAddButton'
2011
import { editorStyles } from './styles'
2112

2213
type EditorProps = {
@@ -25,108 +16,13 @@ type EditorProps = {
2516

2617
export default function Editor(props: EditorProps): React.ReactElement {
2718
const { isNested } = props
28-
29-
const { allApps, isDrawerOpen, isMobile, flow, readOnly, currentStepId } =
30-
useContext(EditorContext)
31-
32-
const { handleReorderUpdate } = useReorderSteps(flow.id)
33-
const steps = flow.steps
34-
19+
const { currentStepId, flow } = useContext(EditorContext)
3520
const currentStep = useMemo(() => {
36-
return steps.find((step) => step.id === currentStepId)
37-
}, [currentStepId, steps])
38-
39-
const appsWithActions: IApp[] = allApps.filter(
40-
(app: IApp) => !!app.actions?.length,
41-
)
42-
43-
const groupingActions = useMemo(() => {
44-
if (!appsWithActions) {
45-
return null
46-
}
47-
48-
return new Set(
49-
appsWithActions?.flatMap((app) =>
50-
app.actions
51-
?.filter((action) => action.groupsLaterSteps)
52-
?.map((action) => `${app.key}-${action.key}`),
53-
) ?? [],
54-
)
55-
}, [appsWithActions])
56-
57-
const [triggerStep, actionStepsBeforeGroup, groupedSteps] = useMemo(() => {
58-
if (!groupingActions) {
59-
return [null, [], []]
60-
}
61-
62-
const groupStepIdx = steps.findIndex((step, index) => {
63-
if (
64-
// We ignore the 1st step because it's either a trigger, or a
65-
// step-grouping action that is using a nested Editor to edit steps in
66-
// its group.
67-
index === 0 ||
68-
!step.appKey ||
69-
!step.key
70-
) {
71-
return false
72-
}
73-
return groupingActions.has(`${step.appKey}-${step.key}`)
74-
})
75-
76-
let branchesWithSteps: IStep[][] = []
77-
if (groupStepIdx !== -1) {
78-
branchesWithSteps = extractBranchesWithSteps(steps.slice(groupStepIdx), 0)
79-
}
21+
return flow.steps.find((step) => step.id === currentStepId)
22+
}, [currentStepId, flow])
8023

81-
const triggerStep = steps[0]
24+
const { isDrawerOpen, isMobile } = useContext(EditorContext)
8225

83-
return groupStepIdx === -1
84-
? [triggerStep, steps.slice(1), []]
85-
: [triggerStep, steps.slice(1, groupStepIdx), branchesWithSteps]
86-
}, [groupingActions, steps])
87-
88-
const handleReorderSteps = async (reorderedSteps: IStep[]) => {
89-
const stepPositions = reorderedSteps.map((step, index) => ({
90-
id: step.id,
91-
position: index + 2, // trigger position is 1
92-
type: step.type as StepEnumType,
93-
}))
94-
95-
try {
96-
await handleReorderUpdate(stepPositions)
97-
} catch (error) {
98-
console.error(
99-
'Error updating step positions: ',
100-
error,
101-
JSON.stringify(stepPositions),
102-
)
103-
}
104-
}
105-
106-
const nonIfThenActionSteps = actionStepsBeforeGroup.filter(
107-
(step) => step.key !== TOOLBOX_ACTIONS.IfThen,
108-
)
109-
110-
// Disables last add step and hide in-between add step buttons
111-
const hasExactlyOneEmptyActionStep =
112-
nonIfThenActionSteps.length === 1 && !nonIfThenActionSteps[0].appKey
113-
114-
// Disables last add step button but show empty action instead
115-
const hasNoActionSteps = nonIfThenActionSteps.length === 0
116-
const shouldShowEmptyAction = hasNoActionSteps && !groupedSteps.length
117-
// for backwards compatibility where empty step is created
118-
const shouldDisableAddButton =
119-
(hasExactlyOneEmptyActionStep || hasNoActionSteps) && !groupedSteps.length
120-
121-
if (!appsWithActions || !groupingActions) {
122-
return (
123-
<Center height="100vh" position="fixed" width="full" top={0} left={0}>
124-
<PrimarySpinner fontSize="4xl" />
125-
</Center>
126-
)
127-
}
128-
129-
const leftStepPadding = isDrawerOpen ? (isMobile ? 0 : '5rem') : 0
13026
const rightDrawerTransform = isDrawerOpen
13127
? 'translateX(0)'
13228
: 'translateX(100%)'
@@ -144,75 +40,9 @@ export default function Editor(props: EditorProps): React.ReactElement {
14440
backgroundSize: '30px 30px',
14541
}}
14642
>
147-
<MrfContextProvider steps={steps}>
148-
<StepExecutionsToIncludeProvider
149-
groupedSteps={groupedSteps}
150-
triggerStep={triggerStep}
151-
actionStepsBeforeGroup={actionStepsBeforeGroup}
152-
>
153-
<Flex
154-
{...editorStyles.stepHeaderContainer}
155-
flex={isDrawerOpen ? (isMobile ? 0 : 1) : undefined}
156-
px={leftStepPadding}
157-
maxWidth={`calc(100% - ${
158-
isDrawerOpen ? EDITOR_RIGHT_DRAWER_WIDTH : '0px'
159-
})`}
160-
>
161-
{triggerStep && (
162-
<FlowStepWithAddButton
163-
step={triggerStep}
164-
isLastStep={
165-
actionStepsBeforeGroup.length === 0 &&
166-
groupedSteps.length === 0
167-
}
168-
isNested={isNested}
169-
stepsBeforeGroup={[]} // no reason to pass in for this
170-
groupedSteps={groupedSteps}
171-
addButtonProps={{
172-
isHidden: readOnly,
173-
isDisabled: shouldDisableAddButton,
174-
showEmptyAction: shouldShowEmptyAction,
175-
}}
176-
/>
177-
)}
178-
179-
<SortableList
180-
items={actionStepsBeforeGroup}
181-
onChange={handleReorderSteps}
182-
renderItem={(step, isOverlay) => {
183-
const { id, position } = step
184-
return (
185-
<SortableList.Item id={id}>
186-
<Flex
187-
key={`${id}-${position}`}
188-
width={isDrawerOpen || isMobile ? '100%' : 'auto'}
189-
flexDir="column"
190-
position="relative"
191-
>
192-
<FlowStepWithAddButton
193-
step={step}
194-
isLastStep={position === steps.length}
195-
isNested={isNested}
196-
stepsBeforeGroup={actionStepsBeforeGroup}
197-
groupedSteps={groupedSteps}
198-
addButtonProps={{
199-
isHidden: readOnly || !!isOverlay,
200-
isDisabled: shouldDisableAddButton,
201-
showEmptyAction: shouldShowEmptyAction,
202-
}}
203-
/>
204-
</Flex>
205-
</SortableList.Item>
206-
)
207-
}}
208-
/>
209-
{groupedSteps.length > 0 && (
210-
<FlowStepGroup
211-
stepsBeforeGroup={actionStepsBeforeGroup}
212-
groupedSteps={groupedSteps}
213-
/>
214-
)}
215-
</Flex>
43+
<MrfContextProvider>
44+
<StepExecutionsToIncludeProvider>
45+
<StepsList isNested={isNested} />
21646
{/** HACKFIX (kevinkim-ogp): to ensure that the transitions are smooth */}
21747
<Flex
21848
{...editorStyles.dummyRightContainer}

packages/frontend/src/components/EditorLayout/index.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,8 +18,8 @@ import { UPDATE_FLOW_STATUS } from '@/graphql/mutations/update-flow-status'
1818
import { GET_FLOW } from '@/graphql/queries/get-flow'
1919
import InvalidEditorPage from '@/pages/Editor/components/InvalidEditorPage'
2020

21+
import UnsavedChangesAlert from '../Editor/components/UnsavedChangesAlert'
2122
import { EDITOR_MARGIN_TOP } from '../Editor/constants'
22-
import UnsavedChangesAlert from '../Editor/UnsavedChangesAlert'
2323

2424
import AnnouncementModal, {
2525
LATEST_ANNOUNCEMENT_MODAL_TIMESTAMP,

0 commit comments

Comments
 (0)