Skip to content

Commit

Permalink
feat: MyInfo Children compound fields beta (#6523)
Browse files Browse the repository at this point in the history
* feat: MyInfo Children compound fields beta

* chore: bump myinfo-gov-client to 4.1.1
  • Loading branch information
kenjin-work authored Jul 19, 2023
1 parent 23cb4c9 commit 6e3f690
Show file tree
Hide file tree
Showing 54 changed files with 1,759 additions and 62 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import Tooltip from '~components/Tooltip'
import {
AttachmentField,
CheckboxField,
ChildrenCompoundField,
DateField,
DecimalField,
DropdownField,
Expand Down Expand Up @@ -495,5 +496,7 @@ const FieldRow = ({ field, ...rest }: FieldRowProps) => {
return <YesNoField schema={field} {...rest} />
case BasicField.Table:
return <TableField schema={field} {...rest} />
case BasicField.Children:
return <ChildrenCompoundField schema={field} {...rest} />
}
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
import { memo, useMemo } from 'react'

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

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

import { useBuilderFields } from '../../BuilderAndDesignContent/useBuilderFields'
import {
Expand All @@ -16,6 +21,10 @@ import {
} from '../../useFieldBuilderStore'
import { BuilderDrawerContainer } from '../common/BuilderDrawerContainer'

import {
ChildrenCompoundFieldMyInfo,
EditMyInfoChildren,
} from './edit-fieldtype/EditMyInfoChildren'
import {
EditAttachment,
EditCheckbox,
Expand Down Expand Up @@ -98,7 +107,19 @@ interface MemoFieldDrawerContentProps {

export const MemoFieldDrawerContent = memo<MemoFieldDrawerContentProps>(
({ field, ...props }) => {
const { user } = useUser()
if (isMyInfo(field)) {
if (
field?.myInfo?.attr === MyInfoAttribute.ChildrenBirthRecords &&
user?.betaFlags?.children
) {
return (
<EditMyInfoChildren
{...props}
field={field as ChildrenCompoundFieldMyInfo}
/>
)
}
return <EditMyInfo {...props} field={field} />
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { Controller } from 'react-hook-form'
import { BiCheck, BiData, BiX } from 'react-icons/bi'
import { Box, FormControl, HStack, Icon, Text, VStack } from '@chakra-ui/react'
import { extend } from 'lodash'

import { MyInfoChildAttributes } from '~shared/types'

import { SINGPASS_FAQ } from '~constants/links'
import { MultiSelect } from '~components/Dropdown'
import Link from '~components/Link'
import { Toggle } from '~components/Toggle/Toggle'

import { CREATE_MYINFO_CHILDREN_SUBFIELDS_OPTIONS } from '~features/admin-form/create/builder-and-design/constants'

import { CreatePageDrawerContentContainer } from '../../../../../common'
import { FormFieldDrawerActions } from '../common/FormFieldDrawerActions'
import { EditFieldProps } from '../common/types'
import { useEditFieldForm } from '../common/useEditFieldForm'
import { extendWithMyInfo } from '../EditMyInfo/utils'

import { ChildrenCompoundFieldMyInfo } from '.'

const VerifiedIcon = ({ isVerified }: { isVerified: boolean }): JSX.Element => {
return (
<Icon
fontSize="1.5rem"
as={isVerified ? BiCheck : BiX}
color={isVerified ? 'success.500' : 'danger.500'}
/>
)
}

const EDIT_MYINFO_CHILDREN = ['allowMultiple', 'childrenSubFields'] as const

type EditMyInfoChildrenProps = EditFieldProps<ChildrenCompoundFieldMyInfo>
type EditMyInfoChildrenInputs = Pick<
ChildrenCompoundFieldMyInfo,
typeof EDIT_MYINFO_CHILDREN[number]
>

export const EditMyInfoChildren = ({
field,
}: EditMyInfoChildrenProps): JSX.Element => {
const extendedField = extendWithMyInfo(field)
const {
control,
register,
buttonText,
handleUpdateField,
isLoading,
handleCancel,
} = useEditFieldForm<EditMyInfoChildrenInputs, ChildrenCompoundFieldMyInfo>({
field,
transform: {
// MyInfo fields are not editable (except for Child compound field),
// so omit any transformation and output the original field
input: (inputField) => inputField,
output: (formOutput, originalField) =>
extend({}, originalField, formOutput),
},
})

return (
<CreatePageDrawerContentContainer>
<VStack align="flex-start">
<Text textStyle="subhead-1">Data source</Text>
{extendedField.dataSource.map((dataSource, idx) => (
<HStack key={idx} align="flex-start">
<Icon fontSize="1.5rem" as={BiData}></Icon>
<Text>{dataSource}</Text>
</HStack>
))}
</VStack>
<VStack align="flex-start">
<Text textStyle="subhead-1">Verified for</Text>
{/* NOTE: Not creating an array from the keys then enumerating because order has to be enforced in UI.
* This allows the object to be created with arbitrary ordered keys.
*/}
<HStack>
<VerifiedIcon isVerified={extendedField.verifiedFor.singaporeans} />
<Text>Singaporeans</Text>
</HStack>
<HStack>
<VerifiedIcon isVerified={extendedField.verifiedFor.pr} />
<Text>Permanent Residents</Text>
</HStack>
<HStack>
<VerifiedIcon
isVerified={extendedField.verifiedFor.singpassforeigners}
/>
<Text>
Foreigners with{' '}
<Link isExternal href={SINGPASS_FAQ}>
Singpass
</Link>
</Text>
</HStack>
</VStack>
<VStack align="flex-start">
<Text textStyle="subhead-1">
Collect the following child information
</Text>
<Box alignSelf="stretch">
<Controller
control={control}
name="childrenSubFields"
render={({ field: { value, onChange, ...rest } }) => (
<MultiSelect
items={CREATE_MYINFO_CHILDREN_SUBFIELDS_OPTIONS}
values={
(value ?? [MyInfoChildAttributes.ChildName]) as string[]
}
// Always insert name
onChange={(val) =>
onChange([
MyInfoChildAttributes.ChildName,
...val.filter(
(val) => val !== MyInfoChildAttributes.ChildName,
),
])
}
{...rest}
/>
)}
/>
</Box>
</VStack>
<VStack align="flex-start">
<FormControl isReadOnly={isLoading}>
<Toggle
{...register('allowMultiple')}
label="Allow respondent to add multiple children"
/>
</FormControl>
</VStack>
<VStack align="flex-start">
<Text textStyle="subhead-1">Field details</Text>
<Text>{extendedField.details}</Text>
</VStack>
<FormFieldDrawerActions
isLoading={isLoading}
buttonText={buttonText}
handleClick={handleUpdateField}
handleCancel={handleCancel}
/>
</CreatePageDrawerContentContainer>
)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { SetRequired } from 'type-fest'

import { ChildrenCompoundFieldBase } from '~shared/types'

export * from './EditMyInfoChildren'

export type ChildrenCompoundFieldMyInfo = SetRequired<
ChildrenCompoundFieldBase,
'myInfo'
>
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@ import InlineMessage from '~components/InlineMessage'
import Link from '~components/Link'

import {
CREATE_MYINFO_CHILDREN_DROP_ID,
CREATE_MYINFO_CHILDREN_FIELDS_ORDERED,
CREATE_MYINFO_CONTACT_DROP_ID,
CREATE_MYINFO_CONTACT_FIELDS_ORDERED,
CREATE_MYINFO_MARRIAGE_DROP_ID,
Expand All @@ -21,6 +23,7 @@ import {
CREATE_MYINFO_PERSONAL_FIELDS_ORDERED,
} from '~features/admin-form/create/builder-and-design/constants'
import { isMyInfo } from '~features/myinfo/utils'
import { useUser } from '~features/user/queries'

import { useCreateTabForm } from '../../../../builder-and-design/useCreateTabForm'
import { DraggableMyInfoFieldListOption } from '../FieldListOption'
Expand All @@ -41,6 +44,7 @@ export const MyInfoFieldPanel = () => {
[form],
)
const isDisabled = isMyInfoDisabled || isLoading
const { user } = useUser()

return (
<>
Expand Down Expand Up @@ -115,6 +119,27 @@ export const MyInfoFieldPanel = () => {
</Box>
)}
</Droppable>
{user?.betaFlags?.children ? (
<Droppable isDropDisabled droppableId={CREATE_MYINFO_CHILDREN_DROP_ID}>
{(provided) => (
<Box ref={provided.innerRef} {...provided.droppableProps}>
<FieldSection label="Family (Children)">
{CREATE_MYINFO_CHILDREN_FIELDS_ORDERED.map(
(fieldType, index) => (
<DraggableMyInfoFieldListOption
index={index}
isDisabled={isDisabled}
key={index}
fieldType={fieldType}
/>
),
)}
</FieldSection>
<Box display="none">{provided.placeholder}</Box>
</Box>
)}
</Droppable>
) : null}
</>
)
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ import { BuilderAndDesignDrawer } from './BuilderAndDesignDrawer'
import {
BASIC_FIELDS_ORDERED,
CREATE_FIELD_DROP_ID,
CREATE_MYINFO_CHILDREN_DROP_ID,
CREATE_MYINFO_CHILDREN_FIELDS_ORDERED,
CREATE_MYINFO_CONTACT_DROP_ID,
CREATE_MYINFO_CONTACT_FIELDS_ORDERED,
CREATE_MYINFO_MARRIAGE_DROP_ID,
Expand Down Expand Up @@ -122,6 +124,15 @@ export const BuilderAndDesignTab = (): JSX.Element => {
)
}

case CREATE_MYINFO_CHILDREN_DROP_ID: {
return setToCreating(
getMyInfoFieldCreationMeta(
CREATE_MYINFO_CHILDREN_FIELDS_ORDERED[source.index],
),
destination.index,
)
}

case FIELD_LIST_DROP_ID: {
if (destination.index === source.index) {
return
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
import {
BasicField,
ChildrenCompoundFieldBase,
DateFieldBase,
DropdownFieldBase,
MobileFieldBase,
MyInfoAttribute,
MyInfoChildAttributes,
ShortTextFieldBase,
} from '~shared/types/field'

import { MyInfoFieldMeta } from '~features/myinfo/types'

import { MYINFO_FIELD_TO_DRAWER_META } from '../constants'

export const BASIC_FIELDS_ORDERED = [
BasicField.ShortText,
BasicField.LongText,
Expand Down Expand Up @@ -61,6 +65,8 @@ export const MYINFO_FIELDS_ORDERED: MyInfoAttribute[] = [
MyInfoAttribute.MarriageCertNo,
MyInfoAttribute.MarriageDate,
MyInfoAttribute.DivorceDate,
// Children section
MyInfoAttribute.ChildrenBirthRecords,
]

export const MYINFO_TEXTFIELD_META: MyInfoFieldMeta<ShortTextFieldBase> = {
Expand All @@ -86,6 +92,12 @@ export const MYINFO_DATEFIELD_META: MyInfoFieldMeta<DateFieldBase> = {
},
}

export const MYINFO_CHILDRENFIELD_META: MyInfoFieldMeta<ChildrenCompoundFieldBase> =
{
childrenSubFields: [MyInfoChildAttributes.ChildName],
allowMultiple: false,
}

export const CREATE_MYINFO_PERSONAL_FIELDS_ORDERED =
MYINFO_FIELDS_ORDERED.slice(0, 13)

Expand All @@ -100,6 +112,9 @@ export const CREATE_MYINFO_PARTICULARS_FIELDS_ORDERED =
export const CREATE_MYINFO_MARRIAGE_FIELDS_ORDERED =
MYINFO_FIELDS_ORDERED.slice(19, 24)

export const CREATE_MYINFO_CHILDREN_FIELDS_ORDERED =
MYINFO_FIELDS_ORDERED.slice(24, 25)

export const CREATE_FIELD_DROP_ID = 'create-fields-field'

export const CREATE_MYINFO_PERSONAL_DROP_ID = 'create-myinfo-personal'
Expand All @@ -109,6 +124,9 @@ export const CREATE_MYINFO_CONTACT_DROP_ID = 'create-myinfo-drop'
export const CREATE_MYINFO_PARTICULARS_DROP_ID = 'create-myinfo-particulars'

export const CREATE_MYINFO_MARRIAGE_DROP_ID = 'create-myinfo-marriage'

export const CREATE_MYINFO_CHILDREN_DROP_ID = 'create-myinfo-children'

export const FIELD_LIST_DROP_ID = 'formFieldList'
export const PENDING_CREATE_FIELD_ID = 'FIELD-PENDING-CREATION'

Expand All @@ -117,3 +135,15 @@ export enum FieldListTabIndex {
MyInfo,
Payments,
}

export const CREATE_MYINFO_CHILDREN_SUBFIELDS_OPTIONS: {
value: MyInfoChildAttributes
label: string
}[] = Object.values(MyInfoChildAttributes)
.filter((e) => e !== MyInfoChildAttributes.ChildName)
.map((value) => {
return {
value,
label: MYINFO_FIELD_TO_DRAWER_META[value].label,
}
})
Loading

0 comments on commit 6e3f690

Please sign in to comment.