Skip to content

Commit

Permalink
feat: approvals for mrf (#7636)
Browse files Browse the repository at this point in the history
* feat: add empty UT for approvals

* feat: add approval outcome email template

* feat: create common FormStepWithHeader component for editBlock items

* feat: approval toggle and field in edit workflow step block

* feat: enable saving of approval_step

* feat: add validation of approval field selection

* feat: add use for approvals badge for yes/no field

* feat: implement sending of approval emails in BE

* feat: validate that approval fields are yes/no on update

* fix: out of bounds index error when re-submitting mrf via response link

* fix: watch values needed for validation

* fix: add default values for sendMrfOutcomeEmails isApproval and isRejected

* feat: validate that approval field must be in same step's edit fields

* feat: log error when checkIsStepRejected fails

* fix: remove duplicates in email sending using uniq

* feat: create REST api for workflow steps

* feat: implement workflow step validation

* feat: display field deleted state

* chore: remove stray comment

* feat: clear value if deleted

* feat: add joi validation for mongodb objectId of hex and 24 length string

* fix: remove workflow from updateSettings

* feat: implement mrf builder v8

* feat: handle error message when admin does not select respondent type radio

* chore: improve rationale comment for getValueIfNotDeleted

* feat: make error badge more explicit by making it red

* feat: only display use for approvals badge for mrf

* feat: add storybook for FieldListDrawer to visual test use for approvals

* feat: add chromatic stories for mrf edit step block

* feat: add chromatic stories for inactive workflow builder state

* feat: add BE validation that first step of MRF workflow cannot be approval

* feat: add BE unit tests for approvals

* chore: add beta flags for approvals

* fix: chromatic visual nits

* feat: add header to mrf email notif

* fix: make workflow content info box 44px

* chore: Remove capitalisations for Outcome and Notifications to comply with design

* chore: remove unused props

* chore: remove stray _ in log action

* chore: remove unused imports

* fix: pr comments

* fix: use edit name across form

* feat: refactor mrf controller to move business logic to service

* feat: add UT for controller and service for mrf

* fix: remove throw error in map

* fix: allow empty lcov

---------

Co-authored-by: Ken <[email protected]>
  • Loading branch information
kevin9foong and KenLSM authored Sep 26, 2024
1 parent 369495a commit c046c43
Show file tree
Hide file tree
Showing 46 changed files with 3,870 additions and 1,065 deletions.
1 change: 1 addition & 0 deletions frontend/src/components/Badge/Badge.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export interface BadgeProps extends ChakraBadgeProps {
* The theme of the tag to display
*/
variant?: BadgeVariants
colorScheme?: string
}

export const Badge = (props: BadgeProps): JSX.Element => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -95,6 +95,7 @@ export const FormLabel = ({
>
<Icon
ml="0.5rem"
mb="0.1rem"
color="secondary.500"
as={tooltipVariant === 'info' ? BxsInfoCircle : BxsHelpCircle}
verticalAlign="middle"
Expand Down
6 changes: 6 additions & 0 deletions frontend/src/components/Radio/Radio.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,10 @@ export interface RadioProps
* please use the props `maxWidth` or `width` to configure
*/
isFullWidth?: boolean
/**
* If `true`, the radio children will occupy the full width of the radio container.
*/
isLabelFullWidth?: boolean
/**
* Background and shadow colors.
*/
Expand Down Expand Up @@ -121,6 +125,7 @@ export const Radio = forwardRef<RadioProps, 'input'>(
spacing = '0.5rem',
children,
isFullWidth,
isLabelFullWidth = false,
...rest
} = omitThemingProps(props)

Expand Down Expand Up @@ -203,6 +208,7 @@ export const Radio = forwardRef<RadioProps, 'input'>(
}

const labelStyles: SystemStyleObject = {
width: isLabelFullWidth ? 'full' : undefined,
userSelect: 'none',
marginStart: spacing,
...styles.label,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { DragDropContext } from 'react-beautiful-dnd'
import { StoryFn } from '@storybook/react'

import { FormResponseMode } from '~shared/types'

import { getAdminFormView } from '~/mocks/msw/handlers/admin-form'

import { StoryRouter } from '~utils/storybook'

import { CreatePageSidebarProvider } from '~features/admin-form/create/common'

import { FieldListDrawer } from '..'

export default {
component: FieldListDrawer,
title:
'Features/AdminForm/create/builder-and-design/BuilderAndDesignDrawer/FieldListDrawer',
parameters: {
msw: [getAdminFormView({ mode: FormResponseMode.Encrypt })],
},
decorators: [
StoryRouter({ initialEntries: ['/12345'], path: '/:formId' }),
(Story: StoryFn) => (
// eslint-disable-next-line @typescript-eslint/no-empty-function
<DragDropContext onDragEnd={() => {}}>
<CreatePageSidebarProvider>
<Story />
</CreatePageSidebarProvider>
</DragDropContext>
),
],
}

const encryptModeHandlers = [
getAdminFormView({ mode: FormResponseMode.Encrypt }),
]

const mrfModeHandlers = [
getAdminFormView({ mode: FormResponseMode.Multirespondent }),
]

export const EncryptMode = {
parameters: {
msw: encryptModeHandlers,
},
}

export const MrfMode = {
parameters: {
msw: mrfModeHandlers,
},
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,14 +6,17 @@ import {
} from 'react-beautiful-dnd'
import { Box, BoxProps, forwardRef, Icon, Stack, Text } from '@chakra-ui/react'

import { FormResponseMode } from '~shared/types'
import { BasicField, MyInfoAttribute } from '~shared/types/field'

import { useIsMobile } from '~hooks/useIsMobile'
import Badge from '~components/Badge'

import {
BASICFIELD_TO_DRAWER_META,
MYINFO_FIELD_TO_DRAWER_META,
} from '~features/admin-form/create/constants'
import { useUser } from '~features/user/queries'

import { useCreateTabForm } from '../../useCreateTabForm'
import {
Expand Down Expand Up @@ -68,7 +71,6 @@ interface DraggableMyInfoFieldOptionProps
export const DraggableBasicFieldListOption = ({
fieldType,
index,
children,
isDisabled,
...props
}: DraggableBasicFieldOptionProps): JSX.Element => {
Expand Down Expand Up @@ -111,7 +113,6 @@ export const DraggableBasicFieldListOption = ({
export const DraggableMyInfoFieldListOption = ({
fieldType,
index,
children,
isDisabled,
...props
}: DraggableMyInfoFieldOptionProps): JSX.Element => (
Expand Down Expand Up @@ -149,12 +150,16 @@ export const DraggableMyInfoFieldListOption = ({

export const BasicFieldOption = forwardRef<BasicFieldOptionProps, 'button'>(
({ fieldType, isDisabled, ...props }, ref) => {
// TODO: (MRF-email-notif) Remove isTest and useUser when approvals is out of beta
const isTest = process.env.NODE_ENV === 'test'
const { user } = useUser()
const meta = useMemo(
() => BASICFIELD_TO_DRAWER_META[fieldType],
[fieldType],
)
const { data: form } = useCreateTabForm()
const numFields = useMemo(() => form?.form_fields?.length ?? 0, [form])
const isMrf = form?.responseMode === FormResponseMode.Multirespondent
const numFields = form?.form_fields?.length ?? 0

const newFieldMeta = useMemo(
() => getFieldCreationMeta(fieldType),
Expand All @@ -178,6 +183,14 @@ export const BasicFieldOption = forwardRef<BasicFieldOptionProps, 'button'>(
>
<Icon fontSize="1.5rem" as={meta.icon} />
<Text textStyle="body-1">{meta.label}</Text>
{/* TODO: (MRF-email-notif) Remove isTest and betaFlag check when approvals is out of beta */}
{isTest || user?.betaFlags?.mrfEmailNotifications ? (
isMrf && fieldType === BasicField.YesNo ? (
<Badge maxW="100%" variant="subtle" colorScheme="secondary">
<Text noOfLines={1}>Use for approvals</Text>
</Badge>
) : null
) : null}
</FieldListOption>
)
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,10 +28,8 @@ export const SaveActionGroup = ({
<Flex
justify="space-between"
align="center"
py="0.375rem"
px={{ base: '1rem', md: '2rem' }}
borderTop="1px solid"
borderColor="neutral.300"
mt="1.5rem"
px={{ base: '1.5rem', md: '2rem' }}
>
{handleDelete ? (
<IconButton
Expand All @@ -51,7 +49,7 @@ export const SaveActionGroup = ({
w="100%"
>
<Button
isLoading={isLoading}
isDisabled={isLoading}
onClick={handleSubmit}
isFullWidth={isMobile}
>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,10 @@ interface FieldLogicBadgeProps {
*/
export const FieldLogicBadge = ({
field,
defaults = { variant: 'error', message: 'Field not found' },
defaults = {
variant: 'error',
message: 'This field was deleted and has been removed from your workflow',
},
}: FieldLogicBadgeProps) => {
const fieldMeta = useMemo(
() => (field ? BASICFIELD_TO_DRAWER_META[field.fieldType] : null),
Expand All @@ -41,6 +44,14 @@ export const FieldLogicBadge = ({
}
}, [defaults.variant, fieldMeta])

const badgeColorScheme = useMemo(() => {
if (fieldMeta) return undefined
if (defaults.variant === 'error') {
return 'danger'
}
return undefined
}, [defaults.variant, fieldMeta])

const tooltipLabel = useMemo(
() => (!fieldMeta ? defaults.message : `${fieldMeta.label} field`),
[fieldMeta, defaults.message],
Expand All @@ -57,7 +68,7 @@ export const FieldLogicBadge = ({
}, [defaults.variant, fieldMeta])

return (
<LogicBadge display="inline-flex">
<LogicBadge display="inline-flex" colorScheme={badgeColorScheme}>
<Stack direction="row" spacing="0.25rem" maxW="100%" align="center">
<Tooltip placement="top" label={tooltipLabel}>
<Box display="inline-flex" alignItems="center">
Expand Down
Loading

0 comments on commit c046c43

Please sign in to comment.