Skip to content

Commit

Permalink
refactor: reorg replacements to reflect form file hierarchy
Browse files Browse the repository at this point in the history
LoneRifle committed Mar 7, 2024

Verified

This commit was created on GitHub.com and signed with GitHub’s verified signature.
1 parent c33c6d1 commit ecb1a4d
Showing 18 changed files with 1,778 additions and 109 deletions.
14 changes: 2 additions & 12 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
@@ -29,18 +29,8 @@ jobs:
run: mv demos/* .
- name: Replace files with demo-specific ones
run: |
mv replacements/GovtMasthead/* frontend/src/components/GovtMasthead
mv replacements/FormLogo/* frontend/src/features/public-form/components/FormLogo
mv replacements/create/* frontend/src/features/admin-form/create
mv replacements/DesignDrawer/* frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/DesignDrawer
mv replacements/EditMobile/* frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditMobile
mv replacements/locales/* frontend/src/i18n/locales
mv replacements/Home/* frontend/src/pages/Landing/Home
mv replacements/login/* frontend/src/features/login/components
mv replacements/public-form/* frontend/src/features/public-form
mv replacements/express/* src/app/loaders/express
mv replacements/utils/* src/app/utils
mv replacements/field/* shared/types/field
cp -rf replacements/* .
rm -rf replacements
- name: Substitute index.html OG params (delivered by Fly's proxy)
run: |
97 changes: 0 additions & 97 deletions replacements/field/base.ts

This file was deleted.

File renamed without changes.
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,174 @@
import { memo, useMemo } from 'react'

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 {
FieldBuilderState,
stateDataSelector,
useFieldBuilderStore,
} from '../../useFieldBuilderStore'
import { BuilderDrawerContainer } from '../common/BuilderDrawerContainer'

import {
ChildrenCompoundFieldMyInfo,
EditMyInfoChildren,
} from './edit-fieldtype/EditMyInfoChildren'
import {
EditAttachment,
EditCheckbox,
EditCountryRegion,
EditDate,
EditDecimal,
EditDropdown,
EditEmail,
EditHeader,
EditHomeno,
EditImage,
EditLongText,
EditMobile,
EditMyInfo,
EditNric,
EditNumber,
EditParagraph,
EditRadio,
EditRating,
EditShortText,
EditTable,
EditUen,
EditYesNo,
} from './edit-fieldtype'

export const EditFieldDrawer = (): JSX.Element | null => {
const stateData = useFieldBuilderStore(stateDataSelector)

const fieldToEdit: FieldCreateDto | undefined = useMemo(() => {
if (
stateData.state === FieldBuilderState.EditingField ||
stateData.state === FieldBuilderState.CreatingField
) {
return stateData.field
}
}, [stateData])

const basicFieldText = useMemo(() => {
if (!fieldToEdit?.fieldType) return ''
if (isMyInfo(fieldToEdit)) {
return MYINFO_FIELD_TO_DRAWER_META[fieldToEdit.myInfo.attr].label
}
return BASICFIELD_TO_DRAWER_META[fieldToEdit?.fieldType].label
}, [fieldToEdit])

// Hacky method of determining when to rerender the drawer,
// i.e. when the user clicks into a different field.
// We pass `${fieldIndex}-${numFields}` as the key. If the
// user was creating a new field but clicked into an existing
// field, causing the new field to be discarded, then numFields
// changes. If the user was editing an existing field then clicked
// into another existing field, causing the edits to be discarded,
// then fieldIndex changes.
const { builderFields } = useBuilderFields()
const fieldIndex = useMemo(() => {
if (stateData.state === FieldBuilderState.CreatingField) {
return stateData.insertionIndex
} else if (stateData.state === FieldBuilderState.EditingField) {
return builderFields?.findIndex(
(field) => field._id === stateData.field._id,
)
}
}, [builderFields, stateData])
const numFields = useMemo(() => builderFields?.length, [builderFields])

if (!fieldToEdit) return null

return (
<BuilderDrawerContainer title={basicFieldText}>
<MemoFieldDrawerContent
field={fieldToEdit}
key={`${fieldIndex}-${numFields}`}
/>
</BuilderDrawerContainer>
)
}

interface MemoFieldDrawerContentProps {
field: FieldCreateDto
}

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} />
}

switch (field.fieldType) {
case BasicField.Attachment:
return <EditAttachment {...props} field={field} />
case BasicField.Checkbox:
return <EditCheckbox {...props} field={field} />
case BasicField.Dropdown:
return <EditDropdown {...props} field={field} />
case BasicField.CountryRegion:
return <EditCountryRegion {...props} field={field} />
case BasicField.Mobile:
return <EditMobile {...props} field={field} />
case BasicField.HomeNo:
return <EditHomeno {...props} field={field} />
case BasicField.Email:
return <EditEmail {...props} field={field} />
case BasicField.Nric:
return <EditNric {...props} field={field} />
case BasicField.Number:
return <EditNumber {...props} field={field} />
case BasicField.Date:
return <EditDate {...props} field={field} />
case BasicField.Decimal:
return <EditDecimal {...props} field={field} />
case BasicField.Section:
return <EditHeader {...props} field={field} />
case BasicField.Uen:
return <EditUen {...props} field={field} />
case BasicField.YesNo:
return <EditYesNo {...props} field={field} />
case BasicField.Radio:
return <EditRadio {...props} field={field} />
case BasicField.Rating:
return <EditRating {...props} field={field} />
case BasicField.ShortText:
return <EditShortText {...props} field={field} />
case BasicField.Table:
return <EditTable {...props} field={field} />
case BasicField.LongText:
return <EditLongText {...props} field={field} />
case BasicField.Statement:
return <EditParagraph {...props} field={field} />
// case BasicField.Image:
// return <EditImage {...props} field={field} />
default:
return <div>TODO: Insert field options here</div>
}
},
)
File renamed without changes.
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
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,
BasicField.Radio,
BasicField.Checkbox,
BasicField.Dropdown,
BasicField.CountryRegion,
BasicField.Section,
BasicField.Statement,
BasicField.YesNo,
BasicField.Rating,
BasicField.Email,
BasicField.Mobile,
BasicField.HomeNo,
BasicField.Date,
// BasicField.Image,
BasicField.Table,
BasicField.Attachment,
BasicField.Number,
BasicField.Decimal,
BasicField.Nric,
BasicField.Uen,
]

export const MYINFO_FIELDS_ORDERED: MyInfoAttribute[] = [
// Personal section
MyInfoAttribute.Name,
MyInfoAttribute.Sex,
MyInfoAttribute.DateOfBirth,
MyInfoAttribute.Race,
MyInfoAttribute.Nationality,
MyInfoAttribute.BirthCountry,
MyInfoAttribute.ResidentialStatus,
MyInfoAttribute.Dialect,
MyInfoAttribute.HousingType,
MyInfoAttribute.HdbType,
MyInfoAttribute.PassportNumber,
MyInfoAttribute.PassportExpiryDate,
MyInfoAttribute.VehicleNo,
// Contact section
MyInfoAttribute.RegisteredAddress,
MyInfoAttribute.MobileNo,
// Particulars section
MyInfoAttribute.Occupation,
MyInfoAttribute.Employment,
MyInfoAttribute.WorkpassStatus,
MyInfoAttribute.WorkpassExpiryDate,
// Family (Marriage) section
MyInfoAttribute.Marital,
MyInfoAttribute.CountryOfMarriage,
MyInfoAttribute.MarriageCertNo,
MyInfoAttribute.MarriageDate,
MyInfoAttribute.DivorceDate,
// Children section
MyInfoAttribute.ChildrenBirthRecords,
]

export const MYINFO_TEXTFIELD_META: MyInfoFieldMeta<ShortTextFieldBase> = {
ValidationOptions: {
selectedValidation: null,
customVal: null,
},
}

export const MYINFO_DROPDOWNFIELD_META: MyInfoFieldMeta<DropdownFieldBase> = {
fieldOptions: [],
}
export const MYINFO_MOBILEFIELD_META: MyInfoFieldMeta<MobileFieldBase> = {
allowIntlNumbers: false,
isVerifiable: false,
}

export const MYINFO_DATEFIELD_META: MyInfoFieldMeta<DateFieldBase> = {
dateValidation: {
customMaxDate: null,
customMinDate: null,
selectedDateValidation: null,
},
}

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)

export const CREATE_MYINFO_CONTACT_FIELDS_ORDERED = MYINFO_FIELDS_ORDERED.slice(
13,
15,
)

export const CREATE_MYINFO_PARTICULARS_FIELDS_ORDERED =
MYINFO_FIELDS_ORDERED.slice(15, 19)

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'

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'

export enum FieldListTabIndex {
Basic = 0,
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,
}
})
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
148 changes: 148 additions & 0 deletions replacements/shared/constants/field/basic.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
import { BasicField } from '../../types/field'

type BasicFieldBlock = {
/** Type of field */
name: BasicField
/** Default name of field */
value: string
/** Whether field is to be submittable */
submitted: boolean
/** Whether field is multi-answer */
answerArray: boolean
}

export const types: BasicFieldBlock[] = [
{
name: BasicField.Section,
value: 'Header',
submitted: true,
answerArray: false,
},
{
name: BasicField.Statement,
value: 'Statement',
submitted: false,
answerArray: false,
},
{
name: BasicField.Email,
value: 'Email',
submitted: true,
answerArray: false,
},
{
name: BasicField.Mobile,
value: 'Mobile Number',
submitted: true,
answerArray: false,
},
{
name: BasicField.HomeNo,
value: 'Home Number',
submitted: true,
answerArray: false,
},
{
name: BasicField.Number,
value: 'Number',
submitted: true,
answerArray: false,
},
{
name: BasicField.Decimal,
value: 'Decimal',
submitted: true,
answerArray: false,
},
// {
// name: BasicField.Image,
// value: 'Image',
// submitted: false,
// answerArray: false,
// },
{
name: BasicField.ShortText,
value: 'Short Text',
submitted: true,
answerArray: false,
},
{
name: BasicField.LongText,
value: 'Long Text',
submitted: true,
answerArray: false,
},
{
name: BasicField.Dropdown,
value: 'Dropdown',
submitted: true,
answerArray: false,
},
{
name: BasicField.CountryRegion,
value: 'Country/Region',
submitted: true,
answerArray: false,
},
{
name: BasicField.YesNo,
value: 'Yes/No',
submitted: true,
answerArray: false,
},
{
name: BasicField.Checkbox,
value: 'Checkbox',
submitted: true,
answerArray: true,
},
{
name: BasicField.Radio,
value: 'Radio',
submitted: true,
answerArray: false,
},
{
name: BasicField.Attachment,
value: 'Attachment',
submitted: true,
answerArray: false,
},
{
name: BasicField.Date,
value: 'Date',
submitted: true,
answerArray: false,
},
{
name: BasicField.Rating,
value: 'Rating',
submitted: true,
answerArray: false,
},
{
name: BasicField.Nric,
value: 'NRIC',
submitted: true,
answerArray: false,
},
{
name: BasicField.Uen,
value: 'UEN',
submitted: true,
answerArray: false,
},
{
name: BasicField.Table,
value: 'Table',
submitted: true,
answerArray: true,
},
]

/**
* Array of BasicFields which are not included in the form response (e.g. statement)
*/
export const FIELDS_TO_REJECT: BasicField[] = types
.filter((f) => !f.submitted)
.map((f) => f.name)
File renamed without changes.
1,304 changes: 1,304 additions & 0 deletions replacements/src/app/models/form.server.model.ts

Large diffs are not rendered by default.

File renamed without changes.

0 comments on commit ecb1a4d

Please sign in to comment.