Skip to content

Commit 0a43484

Browse files
committed
add changes after bug bash
1 parent 947f1a6 commit 0a43484

File tree

10 files changed

+165
-46
lines changed

10 files changed

+165
-46
lines changed

packages/backend/src/apps/formsg/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ import triggers from './triggers'
77
const app: IApp = {
88
name: 'FormSG',
99
key: 'formsg',
10+
description: 'Workflow starts when a new form response is received',
1011
iconUrl: '{BASE_URL}/apps/formsg/assets/favicon.svg',
1112
authDocUrl: 'https://guide.plumber.gov.sg/user-guides/triggers/formsg',
1213
baseUrl: 'https://form.gov.sg',

packages/backend/src/apps/scheduler/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import triggers from './triggers'
55
const app: IApp = {
66
name: 'Scheduler',
77
key: 'scheduler',
8+
description: 'Schedule a time for this workflow to begin',
89
iconUrl: '{BASE_URL}/apps/scheduler/assets/favicon.svg',
910
docUrl: 'https://guide.plumber.gov.sg/user-guides/triggers/scheduler',
1011
authDocUrl: 'https://guide.plumber.gov.sg/user-guides/triggers/scheduler',

packages/backend/src/apps/toolbox/actions/if-then/index.ts

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,8 +19,7 @@ function shouldTakeBranch($: IGlobalVariable): boolean {
1919
const action: IRawAction = {
2020
name: 'If-then',
2121
key: ACTION_KEY,
22-
description:
23-
'Creates different sub-pipes that will execute if specified conditions are met',
22+
description: 'Run different actions based off certain conditions',
2423
groupsLaterSteps: true,
2524
arguments: [
2625
{

packages/backend/src/apps/webhook/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ import triggers from './triggers'
55
const app: IApp = {
66
name: 'Webhook',
77
key: 'webhook',
8+
description: 'Workflow begins when Plumber receives data',
89
iconUrl: '{BASE_URL}/apps/webhook/assets/favicon.svg',
910
authDocUrl: 'https://guide.plumber.gov.sg/user-guides/triggers/webhooks',
1011
baseUrl: '',

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

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@ import {
1313
type ConnectionDropdownOption,
1414
optionGenerator,
1515
} from '../FlowStepConfigurationModal/ChooseAndAddConnection'
16-
import { APP_ALLOWING_EMPTY_CONNECTION } from '../FlowStepConfigurationModal/constants'
16+
import {
17+
APP_ALLOWING_EMPTY_CONNECTION,
18+
EXCEL_APP_KEY,
19+
} from '../FlowStepConfigurationModal/constants'
1720

1821
type ChooseConnectionSubstepProps = {
1922
step: IStep
@@ -46,6 +49,18 @@ const formLinkGenerator = (connectionOption: ConnectionDropdownOption) => {
4649
return `https://form.gov.sg/${formId}`
4750
}
4851

52+
const excelFolderLinkGenerator = (userEmail: string) => {
53+
const encodedUserEmail = encodeURIComponent(userEmail)
54+
.replace(/_/g, '%5F') // underscore
55+
.replace(/\./g, '%2E') // period
56+
.replace(/-/g, '%2D') // hyphen
57+
.replace(/\+/g, '%2B') // plus
58+
// console.log('encodedUserEmail', encodedUserEmail)
59+
const link = `https://gccprod-my.sharepoint.com/shared?id=%2Fsites%2FGOVTECH%2Dplumber%2FShared%20Documents%2F${encodedUserEmail}&listurl=https%3A%2F%2Fgccprod%2Esharepoint%2Ecom%2Fsites%2FGOVTECH%2Dplumber%2FShared%20Documents`
60+
// console.log('link', link)
61+
return link
62+
}
63+
4964
function ChooseConnectionSubstep(
5065
props: ChooseConnectionSubstepProps,
5166
): React.ReactElement {
@@ -112,6 +127,11 @@ function ChooseConnectionSubstep(
112127
url: formLinkGenerator(connectionOption),
113128
text: '(View form)',
114129
}
130+
} else if (application.key === EXCEL_APP_KEY) {
131+
connectionLink = {
132+
url: excelFolderLinkGenerator('[email protected]'),
133+
text: '(View folder)',
134+
}
115135
}
116136

117137
// For FormSG, we provide a link to the form for easier reference

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ export function AddStepButton(props: AddStepButtonProps): JSX.Element {
5656
<Divider orientation="vertical" borderColor="base.divider.strong" />
5757
</Box>
5858
<TouchableTooltip
59-
label={isDisabled ? 'Add your first action first' : 'Add step'}
59+
label={isDisabled ? '' : 'Add step'}
6060
placement="right"
6161
display={isDisabled && !isLastStep ? 'none' : 'flex'}
6262
marginX="auto"
@@ -70,6 +70,7 @@ export function AddStepButton(props: AddStepButtonProps): JSX.Element {
7070
size="xs"
7171
color="interaction.sub.default"
7272
borderRadius="full"
73+
pointerEvents={isDisabled ? 'none' : 'auto'}
7374
_hover={{
7475
bg: 'interaction.muted.neutral.hover',
7576
}}

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

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { BiRun, BiSolidBolt } from 'react-icons/bi'
1+
import { BiPlus, BiSolidBolt } from 'react-icons/bi'
22
import { Flex, Icon, Text } from '@chakra-ui/react'
33

44
interface EmptyFlowStepHeaderProps {
@@ -32,11 +32,9 @@ export default function EmptyFlowStepHeader(
3232
bg: 'interaction.muted.neutral.active',
3333
}}
3434
>
35-
<Icon as={isTrigger ? BiSolidBolt : BiRun} boxSize={6} />
35+
<Icon as={isTrigger ? BiSolidBolt : BiPlus} boxSize={6} />
3636
<Text textStyle="subhead-1">
37-
{isTrigger
38-
? 'Choose how you want your workflow to start'
39-
: 'Choose an action'}
37+
{isTrigger ? 'Choose how you want your workflow to start' : 'Add step'}
4038
</Text>
4139
</Flex>
4240
)

packages/frontend/src/components/FlowStepConfigurationModal/ChooseAppAndEvent/ChooseApp.tsx

Lines changed: 54 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { type IAction, IApp, ITrigger } from '@plumber/types'
1+
import type { IAction, IApp, ITrigger } from '@plumber/types'
22

33
import { useCallback, useContext, useMemo } from 'react'
44
import { BiArrowFromRight, BiChevronRight } from 'react-icons/bi'
@@ -14,12 +14,19 @@ import {
1414
import { Badge, ModalCloseButton } from '@opengovsg/design-system-react'
1515
import { groupBy } from 'lodash'
1616

17-
import { getAppFlag } from '@/config/flags'
17+
import { getAppActionFlag, getAppFlag } from '@/config/flags'
1818
import { LaunchDarklyContext } from '@/contexts/LaunchDarkly'
19+
import {
20+
TOOLBOX_ACTIONS,
21+
TOOLBOX_APP_KEY,
22+
useIfThenInitializer,
23+
useIsIfThenSelectable,
24+
} from '@/helpers/toolbox'
1925

2026
import { FlowStepConfigurationContext } from '../FlowStepConfigurationContext'
2127

2228
import FeedbackFooter from './FeedbackFooter'
29+
import ToolboxEvent from './ToolboxEvent'
2330

2431
const OTHERS_CATEGORY = 'Other'
2532

@@ -31,10 +38,12 @@ interface ChooseAppProps {
3138
export default function ChooseApp(props: ChooseAppProps) {
3239
const { apps, onSelectAppEvent } = props
3340
const launchDarkly = useContext(LaunchDarklyContext)
34-
const { patchModalState, isTrigger } = useContext(
41+
const { patchModalState, isTrigger, isLastStep } = useContext(
3542
FlowStepConfigurationContext,
3643
)
37-
const isLoading = launchDarkly.isLoading
44+
45+
const [_, isInitializingIfThen] = useIfThenInitializer()
46+
const isLoading = launchDarkly.isLoading || isInitializingIfThen
3847

3948
const onSelectApp = useCallback(
4049
(app: IApp) => {
@@ -46,6 +55,30 @@ export default function ChooseApp(props: ChooseAppProps) {
4655
[patchModalState],
4756
)
4857

58+
const isIfThenSelectable = useIsIfThenSelectable({ isLastStep })
59+
const filteredToolboxActions = useMemo(() => {
60+
if (isLoading || !launchDarkly.flags) {
61+
return []
62+
}
63+
64+
const ldToolboxAppFlag = getAppFlag(TOOLBOX_APP_KEY)
65+
if (!launchDarkly.flags[ldToolboxAppFlag]) {
66+
return []
67+
}
68+
69+
const toolboxActions =
70+
apps?.find((app) => app.key === TOOLBOX_APP_KEY)?.actions ?? []
71+
return toolboxActions.filter((action) => {
72+
// Filter away actions hidden behind feature flags
73+
if (isLoading || !launchDarkly.flags) {
74+
return true
75+
}
76+
77+
const ldToolboxActionFlag = getAppActionFlag(TOOLBOX_APP_KEY, action.key)
78+
return launchDarkly.flags[ldToolboxActionFlag] ?? true
79+
})
80+
}, [apps, isLoading, launchDarkly.flags])
81+
4982
// Combine filtering and grouping logic into a single operation
5083
const groupedApps = useMemo(() => {
5184
const filteredApps = apps?.filter((app) => {
@@ -107,10 +140,10 @@ export default function ChooseApp(props: ChooseAppProps) {
107140
linear-gradient(rgba(255, 255, 255, 0), white 70%) center bottom,
108141
109142
/* Shadow TOP */
110-
radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0)) center top,
143+
radial-gradient(farthest-side at 50% 0, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0)) center top,
111144
112145
/* Shadow BOTTOM */
113-
radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, 0.2), rgba(0, 0, 0, 0)) center bottom
146+
radial-gradient(farthest-side at 50% 100%, rgba(0, 0, 0, 0.1), rgba(0, 0, 0, 0)) center bottom
114147
`,
115148
backgroundRepeat: 'no-repeat',
116149
backgroundSize: '100% 40px, 100% 40px, 100% 14px, 100% 14px',
@@ -138,6 +171,21 @@ export default function ChooseApp(props: ChooseAppProps) {
138171

139172
<Flex flexDir="column" gap={3}>
140173
{apps.map((app) => {
174+
// For toolbox app specifically, show all the toolbox actions
175+
if (app.key === TOOLBOX_APP_KEY) {
176+
return filteredToolboxActions.map((action) => (
177+
<ToolboxEvent
178+
key={action.key}
179+
action={action}
180+
onSelectAppEvent={() => onSelectAppEvent(app, action)}
181+
isDisabled={
182+
action.key === TOOLBOX_ACTIONS.IfThen &&
183+
!isIfThenSelectable
184+
}
185+
/>
186+
))
187+
}
188+
141189
const triggersOrActions = isTrigger
142190
? app.triggers
143191
: app.actions

packages/frontend/src/components/FlowStepConfigurationModal/ChooseAppAndEvent/ChooseEvent.tsx

Lines changed: 8 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@ import { Button, ModalCloseButton } from '@opengovsg/design-system-react'
77

88
import { getAppActionFlag, getAppTriggerFlag } from '@/config/flags'
99
import { LaunchDarklyContext } from '@/contexts/LaunchDarkly'
10-
import {
11-
TOOLBOX_ACTIONS,
12-
TOOLBOX_APP_KEY,
13-
useIfThenInitializer,
14-
useIsIfThenSelectable,
15-
} from '@/helpers/toolbox'
1610

1711
import { FlowStepConfigurationContext } from '../FlowStepConfigurationContext'
1812
import InvalidModalScreen from '../InvalidModalScreen'
@@ -28,15 +22,12 @@ export default function ChooseEvent(props: ChooseEventProps): JSX.Element {
2822

2923
const launchDarkly = useContext(LaunchDarklyContext)
3024

31-
const { modalState, isTrigger, isLastStep, patchModalState } = useContext(
25+
const { modalState, isTrigger, patchModalState } = useContext(
3226
FlowStepConfigurationContext,
3327
)
3428
const { selectedApp } = modalState
3529

36-
const [_, isInitializingIfThen] = useIfThenInitializer()
37-
const isLoading = launchDarkly.isLoading || isInitializingIfThen
38-
39-
const isIfThenSelectable = useIsIfThenSelectable({ isLastStep })
30+
const isLoading = launchDarkly.isLoading
4031

4132
const filteredTriggersOrActions = useMemo(() => {
4233
if (!selectedApp) {
@@ -97,48 +88,34 @@ export default function ChooseEvent(props: ChooseEventProps): JSX.Element {
9788
<Flex flexDir="column" gap={3}>
9889
{filteredTriggersOrActions?.map(
9990
(triggerOrAction: ITrigger | IAction) => {
100-
const isIfThen =
101-
selectedApp.key === TOOLBOX_APP_KEY &&
102-
triggerOrAction.key === TOOLBOX_ACTIONS.IfThen
103-
const isDisabled = isIfThen && !isIfThenSelectable
104-
10591
return (
10692
<Box
10793
key={triggerOrAction.key}
10894
p={4}
10995
borderWidth="1px"
11096
borderRadius="lg"
111-
onClick={() =>
112-
!isDisabled &&
113-
onSelectAppEvent(selectedApp, triggerOrAction)
114-
}
115-
opacity={isDisabled ? 0.5 : 1}
97+
onClick={() => onSelectAppEvent(selectedApp, triggerOrAction)}
98+
opacity={1}
11699
_hover={{
117100
bg: 'interaction.muted.neutral.hover',
118-
cursor: isDisabled ? 'not-allowed' : 'pointer',
101+
cursor: 'pointer',
119102
}}
120103
_active={{
121104
bg: 'interaction.muted.neutral.active',
122105
}}
123106
_focus={{
124107
outline: 'none',
125-
boxShadow: isDisabled
126-
? 'none'
127-
: '0 0 0 2px var(--chakra-colors-primary-500)',
108+
boxShadow: '0 0 0 2px var(--chakra-colors-primary-500)',
128109
}}
129-
tabIndex={isDisabled ? -1 : 0} // Make focusable unless disabled
110+
tabIndex={0}
130111
onKeyDown={(e) => {
131112
if (e.key === 'Enter') {
132113
onSelectAppEvent(selectedApp, triggerOrAction)
133114
}
134115
}}
135116
>
136117
<Text textStyle="subhead-1">{triggerOrAction.name}</Text>
137-
<Text textStyle="body-2">
138-
{isDisabled
139-
? 'This can only be used in the last step'
140-
: triggerOrAction.description}
141-
</Text>
118+
<Text textStyle="body-2">{triggerOrAction.description}</Text>
142119
</Box>
143120
)
144121
},
Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
import type { IAction } from '@plumber/types'
2+
3+
import { BiFilterAlt, BiGitRepoForked, BiQuestionMark } from 'react-icons/bi'
4+
import { Flex, Icon, Text } from '@chakra-ui/react'
5+
import { TouchableTooltip } from '@opengovsg/design-system-react'
6+
7+
const TOOLBOX_ACTION_TO_ICON_MAP = {
8+
onlyContinueIf: BiFilterAlt,
9+
ifThen: BiGitRepoForked,
10+
}
11+
12+
interface ToolboxEventProps {
13+
action: IAction
14+
onSelectAppEvent: () => void
15+
isDisabled: boolean
16+
}
17+
18+
export default function ToolboxEvent(props: ToolboxEventProps): JSX.Element {
19+
const { action, isDisabled, onSelectAppEvent } = props
20+
return (
21+
<TouchableTooltip
22+
label={isDisabled ? 'This can only be used in the last step' : ''}
23+
placement="bottom"
24+
openDelay={100}
25+
closeDelay={100}
26+
>
27+
<Flex
28+
key={action.key}
29+
p={4}
30+
borderWidth="1px"
31+
borderRadius="lg"
32+
onClick={() => !isDisabled && onSelectAppEvent()}
33+
opacity={isDisabled ? 0.5 : 1}
34+
_hover={{
35+
bg: 'interaction.muted.neutral.hover',
36+
cursor: isDisabled ? 'not-allowed' : 'pointer',
37+
}}
38+
_active={{
39+
bg: 'interaction.muted.neutral.active',
40+
}}
41+
_focus={{
42+
outline: 'none',
43+
boxShadow: isDisabled
44+
? 'none'
45+
: '0 0 0 2px var(--chakra-colors-primary-500)',
46+
}}
47+
tabIndex={isDisabled ? -1 : 0} // Make focusable unless disabled
48+
onKeyDown={(e) => {
49+
if (e.key === 'Enter') {
50+
onSelectAppEvent()
51+
}
52+
}}
53+
>
54+
<Flex alignItems="center" gap={4}>
55+
<Icon
56+
boxSize={8}
57+
as={
58+
TOOLBOX_ACTION_TO_ICON_MAP[
59+
action.key as keyof typeof TOOLBOX_ACTION_TO_ICON_MAP
60+
] ?? BiQuestionMark
61+
}
62+
color="base.content.default"
63+
/>
64+
65+
<Flex flexDir="column" gap={1}>
66+
<Text textStyle="subhead-1">{action.name}</Text>
67+
<Text textStyle="body-2">{action.description}</Text>
68+
</Flex>
69+
</Flex>
70+
</Flex>
71+
</TouchableTooltip>
72+
)
73+
}

0 commit comments

Comments
 (0)