Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion packages/backend/src/graphql/mutations/create-step.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ const createStep: MutationResolvers['createStep'] = async (
throw new BadUserInputError('No such trigger or action')
}

if ('hiddenFromUser' in triggerOrAction && triggerOrAction.hiddenFromUser) {
if (triggerOrAction?.hiddenFromUser) {
throw new BadUserInputError('Action can only be created by system')
}
}
Expand Down
2 changes: 1 addition & 1 deletion packages/backend/src/models/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ class App {
static async findTriggerOrActionByKey(
appKey: string,
key: string,
): Promise<ITrigger | IAction> {
): Promise<ITrigger | IAction | null> {
try {
const app = await this.findOneByKey(appKey)
return (
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import { Button, Link } from '@opengovsg/design-system-react'

import { EditorContext } from '@/contexts/Editor'
import { TEST_CONNECTION } from '@/graphql/queries/test-connection'
import { FORMSG_APP_KEY } from '@/helpers/formsg'
import useAuthentication from '@/hooks/useAuthentication'

import { NON_EDITABLE_APP_CONNECTIONS } from '../Editor/constants'
Expand Down Expand Up @@ -118,7 +119,7 @@ function ChooseConnectionSubstep(
const connectionOption = optionGenerator(connection, application.key)

let connectionLink: ConnectionLink | undefined
if (application.key === 'formsg') {
if (application.key === FORMSG_APP_KEY) {
connectionLink = {
url: formLinkGenerator(connectionOption),
text: '(View form)',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,9 @@ import { useContext, useMemo } from 'react'
import { EditorContext } from '@/contexts/Editor'
import { FlowStep } from '@/exports/components'
import { TOOLBOX_ACTIONS } from '@/helpers/toolbox'
import { useStepMetadata } from '@/hooks/useStepMetadata'

import { ApproveReject } from '../FlowStep/components/ApproveReject'

import { AddStepButton } from './AddStepButton'

Expand All @@ -23,7 +26,9 @@ export default function FlowStepWithAddButton({
groupedSteps: IStep[][]
showAddButton?: boolean
}) {
const { readOnly } = useContext(EditorContext)
const { readOnly, allApps } = useContext(EditorContext)

const { isApprovalStep } = useStepMetadata(allApps, step)

const nonIfThenActionSteps = stepsBeforeGroup.filter(
(step) => step.type === 'action' && step.key !== TOOLBOX_ACTIONS.IfThen,
Expand Down Expand Up @@ -59,12 +64,13 @@ export default function FlowStepWithAddButton({
<>
<FlowStep
step={step}
isDeletable={true}
isLastStep={isLastStep}
isNested={isNested}
// only allow reordering if there are more than 1 action steps
allowReorder={nonIfThenActionSteps.length > 1}
/>
{isApprovalStep && <ApproveReject />}

{showAddButton && (
<AddStepButton {...getAddStepButtonProps(isLastStep, step.id)} />
)}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import { useState } from 'react'
import { Flex, Tab, TabList, TabProps, Tabs } from '@chakra-ui/react'

const tabStyle = (primaryColor: string): TabProps => {
return {
_selected: {
color: 'white',
bg: primaryColor,
_hover: {
color: 'white',
},
},
_hover: {
color: primaryColor,
},
letterSpacing: '0',
fontWeight: 'medium',
textTransform: 'none',
px: 4,
fontSize: 'medium',
}
}

export function ApproveReject() {
const [isApprovedSelected, setIsApprovedSelected] = useState(true)

return (
<Flex mt={4} mx="auto">
<Tabs
variant="soft-rounded"
index={isApprovedSelected ? 0 : 1}
backgroundColor="base.divider.medium"
borderRadius="full"
py={1}
px={0.5}
onChange={(index) => setIsApprovedSelected(index === 0)}
>
<TabList gap={2}>
<Tab {...tabStyle('green.500')}>If approved</Tab>
<Tab {...tabStyle('red.500')}>If rejected</Tab>
</TabList>
</Tabs>
</Flex>
)
}
9 changes: 1 addition & 8 deletions packages/frontend/src/components/FlowStep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import { Box, CircularProgress, Flex, useDisclosure } from '@chakra-ui/react'
import { Infobox } from '@opengovsg/design-system-react'

import { EditorContext } from '@/contexts/Editor'
import { StepDisplayOverridesContext } from '@/contexts/StepDisplayOverrides'
import { MarkdownRenderer } from '@/exports/components'
import { getFlowStepHeaderWidth } from '@/helpers/editor'
import { replacePlaceholdersForHelpMessage } from '@/helpers/flow-templates'
Expand All @@ -31,7 +30,6 @@ import { flowStepStyles } from './styles'

type FlowStepProps = {
step: IStep
isDeletable?: boolean
isLastStep: boolean
isNested?: boolean
allowReorder?: boolean
Expand Down Expand Up @@ -70,7 +68,6 @@ export default function FlowStep(
setCurrentStepId,
setShouldWarnOnLeave,
} = useContext(EditorContext)
const displayOverrides = useContext(StepDisplayOverridesContext)?.[step.id]
const {
app,
caption,
Expand All @@ -79,6 +76,7 @@ export default function FlowStep(
selectedActionOrTrigger,
substeps,
shouldShowDragHandle,
isDeletable,
} = useStepMetadata(
allApps,
step,
Expand All @@ -99,11 +97,6 @@ export default function FlowStep(
onProceed: onModalOpen,
})

const isDeletable =
displayOverrides?.disableDelete === true
? false
: !readOnly && props.isDeletable

const { shouldTestStepAgain, isTestSuccessful } = useMemo(
() => validateStepParams(step, testExecutionSteps, substeps),
[step, substeps, testExecutionSteps],
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,6 @@ import { useContext } from 'react'

import { EditorContext } from '@/contexts/Editor'
import { FlowStep } from '@/exports/components'
import { TOOLBOX_ACTIONS } from '@/helpers/toolbox'

import { HoverAddStepButton } from '../Content/IfThen/HoverAddStepButton'

Expand All @@ -27,20 +26,16 @@ export default function GroupStepWithAddButton(
isLastStep,
isOverlay,
allowReorder,

showEmptyAction,
canChildStepsReorder,
} = props
const { isDrawerOpen, readOnly } = useContext(EditorContext)

// cannot delete the condition step
const isDeletable =
step.key !== TOOLBOX_ACTIONS.IfThen && step.key !== TOOLBOX_ACTIONS.ForEach

return (
<>
<FlowStep
step={step}
isDeletable={isDeletable}
// branch steps are always nested
isNested={true}
isLastStep={isLastStep}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,12 @@ import {
} from '@chakra-ui/react'
import { Button, IconButton, Menu } from '@opengovsg/design-system-react'

import {
FORMSG_APP_KEY,
FORMSG_TRIGGER_KEY,
MRF_ACTION_KEY,
} from '@/helpers/formsg'

interface CheckAgainButtonProps {
isUnstyledInfobox: boolean
onClick: (testRunMetadata?: Record<string, unknown>) => void
Expand All @@ -27,9 +33,9 @@ interface CheckAgainButtonProps {
export function CheckAgainButton(props: CheckAgainButtonProps) {
const { isUnstyledInfobox, onClick, isLoading, isDisabled, step } = props
const isFormSgTrigger =
step.appKey === 'formsg' && step.key === 'newSubmission'
step.appKey === FORMSG_APP_KEY && step.key === FORMSG_TRIGGER_KEY
const isFormSgAction =
step.appKey === 'formsg' && step.key === 'mrfSubmission'
step.appKey === FORMSG_APP_KEY && step.key === MRF_ACTION_KEY

if (isFormSgTrigger) {
return <FormSGCheckAgainButton {...props} />
Expand Down
6 changes: 1 addition & 5 deletions packages/frontend/src/components/FlowSubstep/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -125,11 +125,7 @@ function FlowSubstep(props: FlowSubstepProps): JSX.Element {
const handleSaveAndTest = useCallback(
async (testRunMetadata?: Record<string, unknown>) => {
try {
if (
!selectedActionOrTrigger ||
!('hiddenFromUser' in selectedActionOrTrigger) ||
selectedActionOrTrigger.hiddenFromUser !== true
) {
if (!selectedActionOrTrigger?.hiddenFromUser) {
await saveStep()
}
await executeTestStep({ testRunMetadata })
Expand Down
41 changes: 0 additions & 41 deletions packages/frontend/src/contexts/StepDisplayOverrides.tsx

This file was deleted.

3 changes: 3 additions & 0 deletions packages/frontend/src/helpers/formsg.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export const MRF_ACTION_KEY = 'mrfSubmission'
export const FORMSG_APP_KEY = 'formsg'
export const FORMSG_TRIGGER_KEY = 'newSubmission'
29 changes: 26 additions & 3 deletions packages/frontend/src/hooks/useStepMetadata.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
import { IAction, IApp, IStep, ISubstep, ITrigger } from '@plumber/types'

import { useMemo } from 'react'
import get from 'lodash/get'

import { FORMSG_APP_KEY, MRF_ACTION_KEY } from '@/helpers/formsg'
import getStepName from '@/helpers/getStepName'
import { isIfThenStep as checkIfThenStep } from '@/helpers/toolbox'
import {
isIfThenStep as checkIfThenStep,
TOOLBOX_ACTIONS,
} from '@/helpers/toolbox'

interface UseStepMetadataResult {
app: IApp | undefined
Expand All @@ -18,7 +23,9 @@ interface UseStepMetadataResult {
stepName: string
substeps: ISubstep[]
shouldShowDragHandle?: boolean
isDeletable: boolean
isMrfStep: boolean
isApprovalStep: boolean
}

export function useStepMetadata(
Expand All @@ -32,7 +39,6 @@ export function useStepMetadata(
const isCompleted = step?.status === 'completed'
const isTrigger = step?.type === 'trigger'
const isIfThenStep = step ? checkIfThenStep(step) : false
const isMrfStep = step?.key === 'mrfSubmission'

const apps: IApp[] = allApps?.filter((app: IApp) =>
isTrigger ? !!app.triggers?.length : !!app.actions?.length,
Expand Down Expand Up @@ -60,6 +66,21 @@ export function useStepMetadata(
(substep: ISubstep) => substep.key === 'chooseConnection',
)

const isDeletable = useMemo(
() =>
!readOnly &&
step?.key !== TOOLBOX_ACTIONS.IfThen &&
step?.key !== TOOLBOX_ACTIONS.ForEach &&
!selectedActionOrTrigger?.hiddenFromUser,
[readOnly, selectedActionOrTrigger, step?.key],
)

const isMrfStep =
step?.appKey === FORMSG_APP_KEY && step?.key === MRF_ACTION_KEY

const isApprovalStep =
isMrfStep && !!get(step?.parameters, 'mrf.approvalField', false)

/**
* NOTE: there are various conditions that determine whether the drag handle
* should be shown.
Expand Down Expand Up @@ -90,13 +111,15 @@ export function useStepMetadata(
hasConnection,
isCompleted,
isIfThenStep,
isMrfStep,
isTrigger,
position: step?.position ?? 0,
stepName: step?.config?.stepName
? step.config.stepName
: defaultCaption ?? '',
substeps,
shouldShowDragHandle,
isDeletable,
isMrfStep,
isApprovalStep,
}
}
1 change: 1 addition & 0 deletions packages/types/index.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,7 @@ export interface IBaseTrigger {
description: string
isNew?: boolean
webhookTriggerInstructions?: ITriggerInstructions
hiddenFromUser?: boolean
getInterval?(parameters: IStep['parameters']): string
run?($: IGlobalVariable): Promise<void>
testRun?(
Expand Down