diff --git a/CHANGELOG.md b/CHANGELOG.md
index 08dad0f49c..686e718090 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -4,12 +4,29 @@ All notable changes to this project will be documented in this file. Dates are d
Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
+#### [v6.68.0](https://github.com/opengovsg/FormSG/compare/v6.67.1...v6.68.0)
+
+- chore(deps-dev): bump @types/lodash from 4.14.195 to 4.14.196 in /shared [`#6570`](https://github.com/opengovsg/FormSG/pull/6570)
+- feat: rm bot protection for GSIB users [`#6568`](https://github.com/opengovsg/FormSG/pull/6568)
+- chore: lint and update commands in README [`#6567`](https://github.com/opengovsg/FormSG/pull/6567)
+- feat: MyInfo over sgID (frontend) [`#6447`](https://github.com/opengovsg/FormSG/pull/6447)
+- chore: rm redundant type assertion in url validation [`#6566`](https://github.com/opengovsg/FormSG/pull/6566)
+- feat: updated uen validator (ref stdnum) and tests cases [`#6534`](https://github.com/opengovsg/FormSG/pull/6534)
+- fix: subtle bugs in MyInfo Child and UI copyedits [`#6562`](https://github.com/opengovsg/FormSG/pull/6562)
+- feat: react router params validator for mongo ids [`#6561`](https://github.com/opengovsg/FormSG/pull/6561)
+- build: merge v6.67.1 into develop [`#6559`](https://github.com/opengovsg/FormSG/pull/6559)
+- build: release v6.67.1 [`#6557`](https://github.com/opengovsg/FormSG/pull/6557)
+- fix: github warning about unused import [`#6558`](https://github.com/opengovsg/FormSG/pull/6558)
+
#### [v6.67.1](https://github.com/opengovsg/FormSG/compare/v6.67.0...v6.67.1)
+> 20 July 2023
+
- fix: paynow payout being ignored [`#6556`](https://github.com/opengovsg/FormSG/pull/6556)
- build: release v6.67.0 [`#6555`](https://github.com/opengovsg/FormSG/pull/6555)
+- chore: bump version to 6.67.1 [`b641094`](https://github.com/opengovsg/FormSG/commit/b641094d40a67a888e3a8ac967a43f9d1732bd49)
-#### [v6.67.0](https://github.com/opengovsg/FormSG/compare/v6.66.1...v6.67.0)
+#### [v6.67.0](https://github.com/opengovsg/FormSG/compare/v6.66.0...v6.67.0)
> 20 July 2023
@@ -19,20 +36,8 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- build: release v6.66.0 [`#6550`](https://github.com/opengovsg/FormSG/pull/6550)
- feat: MyInfo Children compound fields beta [`#6523`](https://github.com/opengovsg/FormSG/pull/6523)
- ci: adjust timeouts for playwright pipe [`#6551`](https://github.com/opengovsg/FormSG/pull/6551)
-- fix: use proof-of-payment for payment receipt/invoice [`#6549`](https://github.com/opengovsg/FormSG/pull/6549)
-- build(deps-dev): bump word-wrap from 1.2.3 to 1.2.4 in /frontend [`#6548`](https://github.com/opengovsg/FormSG/pull/6548)
-- fix(deps): bump word-wrap from 1.2.3 to 1.2.4 [`#6547`](https://github.com/opengovsg/FormSG/pull/6547)
-- build(deps): bump winston-cloudwatch to v6.2.0 [`#6545`](https://github.com/opengovsg/FormSG/pull/6545)
-- chore: bump version to v6.66.0 [`0233ba8`](https://github.com/opengovsg/FormSG/commit/0233ba8018532d4978d13717fac065f81e2eb515)
- chore: bump version to v6.67.0 [`6ca03c6`](https://github.com/opengovsg/FormSG/commit/6ca03c68cb162a4ae3af01f172425b3305742ae0)
-#### [v6.66.1](https://github.com/opengovsg/FormSG/compare/v6.66.0...v6.66.1)
-
-> 18 July 2023
-
-- revert: build(deps): bump winston-cloudwatch version to 6.2.0 [`#6542`](https://github.com/opengovsg/FormSG/pull/6542)
-- chore: bump version to v6.66.0 [`b3a9818`](https://github.com/opengovsg/FormSG/commit/b3a9818762e334bc7126c029cdba63156bac85ed)
-
#### [v6.66.0](https://github.com/opengovsg/FormSG/compare/v6.65.0...v6.66.0)
> 19 July 2023
@@ -405,7 +410,7 @@ Generated by [`auto-changelog`](https://github.com/CookPete/auto-changelog).
- fix: only return previous payment id [`#6210`](https://github.com/opengovsg/FormSG/pull/6210)
- build: merge release v6.45.0 to develop [`#6205`](https://github.com/opengovsg/FormSG/pull/6205)
- build: release v6.45.0 [`#6200`](https://github.com/opengovsg/FormSG/pull/6200)
-- chore: bump version to v6.46.0 [`767e7e5`](https://github.com/opengovsg/FormSG/commit/767e7e55c3e8f058963e890b1c249704d6db1459)
+- chore: bump version to v6.46.0 [`87fda0e`](https://github.com/opengovsg/FormSG/commit/87fda0e0ec4c937350ebd013a4a9762f4acee802)
#### [v6.45.0](https://github.com/opengovsg/FormSG/compare/v6.44.1...v6.45.0)
diff --git a/CREDITS.md b/CREDITS.md
index d2029eaa44..ba986f91cc 100644
--- a/CREDITS.md
+++ b/CREDITS.md
@@ -2200,6 +2200,35 @@ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
-------------------------------------------------------------------------------
+## Project
+python_stdnum
+
+### Source
+https://github.com/arthurdejong/python-stdnum/
+
+### License
+uen.py - functions for handling Singapore UEN numbers
+coding: utf-8
+
+Copyright (C) 2020 Leandro Regueiro
+Copyright (C) 2020 Arthur de Jong
+
+This library is free software; you can redistribute it and/or
+modify it under the terms of the GNU Lesser General Public
+License as published by the Free Software Foundation; either
+version 2.1 of the License, or (at your option) any later version.
+
+This library is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+Lesser General Public License for more details.
+
+You should have received a copy of the GNU Lesser General Public
+License along with this library; if not, write to the Free Software
+Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301 USA
+
+
## Project
toastr
diff --git a/README.md b/README.md
index 541882a909..ce8f23e1a3 100755
--- a/README.md
+++ b/README.md
@@ -197,7 +197,7 @@ npm run test:frontend
#### End-to-end tests
```bash
-npm run test-e2e
+npm run test:e2e-v2
```
will build both the frontend and backend then run our end-to-end tests. The tests are located at [`__tests__/e2e`](./__tests__/e2e). You will need to stop the Docker dev container to be able to run the end-to-end tests.
@@ -205,7 +205,7 @@ will build both the frontend and backend then run our end-to-end tests. The test
If you do not need to rebuild the frontend and backend, you can run
```bash
-npm run test-e2e-ci
+npx playwright test
```
#### Cross-browser testing
diff --git a/__tests__/e2e/constants/field.ts b/__tests__/e2e/constants/field.ts
index 6689825aa7..1f51524df8 100644
--- a/__tests__/e2e/constants/field.ts
+++ b/__tests__/e2e/constants/field.ts
@@ -314,7 +314,7 @@ export const ALL_FIELDS: E2eFieldMetadata[] = [
{
title: 'Some UEN here please',
fieldType: BasicField.Uen,
- val: 'T09LL0001B',
+ val: 'T09SS0050A',
},
{
title: 'Yes or No?',
diff --git a/__tests__/e2e/helpers/createForm.ts b/__tests__/e2e/helpers/createForm.ts
index 443cf6ae3a..62fc941040 100644
--- a/__tests__/e2e/helpers/createForm.ts
+++ b/__tests__/e2e/helpers/createForm.ts
@@ -302,18 +302,12 @@ const addAuthSettings = async (
page.getByRole('heading', { name: 'Enable Singpass authentication' }),
).toBeVisible()
- const name = `Singpass${
- formSettings.authType === FormAuthType.SP
- ? ''
- : formSettings.authType === FormAuthType.SGID
- ? ' App-only Login'
- : formSettings.authType === FormAuthType.MyInfo
- ? ' with MyInfo'
- : ' (Corporate)'
- }`
-
await page
- .locator('label', { has: page.getByRole('radio', { name }) })
+ .locator('label', {
+ has: page.locator(
+ `input[type='radio'][value='${formSettings.authType}']`,
+ ),
+ })
.first() // Since 'Singpass' will match all radio options, pick the first matching one.
.click({ position: { x: 1, y: 1 } }) // Clicking the center of the sgid button launches the sgid contact form, put this here until we get rid of the link
diff --git a/frontend/package-lock.json b/frontend/package-lock.json
index 8bc1a55fd5..60aec3a455 100644
--- a/frontend/package-lock.json
+++ b/frontend/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "form-frontend",
- "version": "6.67.0",
+ "version": "6.68.0",
"lockfileVersion": 2,
"requires": true,
"packages": {
"": {
"name": "form-frontend",
- "version": "6.67.0",
+ "version": "6.68.0",
"hasInstallScript": true,
"dependencies": {
"@chakra-ui/react": "^1.8.6",
diff --git a/frontend/package.json b/frontend/package.json
index 20b768ace6..f8cbc954ce 100644
--- a/frontend/package.json
+++ b/frontend/package.json
@@ -1,6 +1,6 @@
{
"name": "form-frontend",
- "version": "6.67.0",
+ "version": "6.68.0",
"homepage": ".",
"private": true,
"dependencies": {
diff --git a/frontend/src/app/AppRouter.tsx b/frontend/src/app/AppRouter.tsx
index b34717e646..9cab6884ec 100644
--- a/frontend/src/app/AppRouter.tsx
+++ b/frontend/src/app/AppRouter.tsx
@@ -40,6 +40,7 @@ import { FormPaymentPage } from '~features/public-form/components/FormPaymentPag
import { BillingPage } from '~features/user/billing'
import { HashRouterElement } from './HashRouterElement'
+import { ParamIdValidator } from './ParamIdValidator'
import { PrivateElement } from './PrivateElement'
import { PublicElement } from './PublicElement'
@@ -109,20 +110,38 @@ export const AppRouter = (): JSX.Element => {
} />}
+ element={
+ } />}
+ />
+ }
/>
} />}
+ element={
+ } />
+ }
+ />
+ }
/>
} />}
+ element={
+ } />}
+ />
+ }
/>
} />}
+ element={
+ } />}
+ />
+ }
>
} />
}>
@@ -147,11 +166,19 @@ export const AppRouter = (): JSX.Element => {
} />}
+ element={
+ } />}
+ />
+ }
/>
} />}
+ element={
+ } />}
+ />
+ }
/>
} />
diff --git a/frontend/src/app/ParamIdValidator.tsx b/frontend/src/app/ParamIdValidator.tsx
new file mode 100644
index 0000000000..34a381f61d
--- /dev/null
+++ b/frontend/src/app/ParamIdValidator.tsx
@@ -0,0 +1,26 @@
+import { useParams } from 'react-router-dom'
+
+import { MONGODB_ID_REGEX } from '~constants/routes'
+
+import NotFoundErrorPage from '~pages/NotFoundError'
+
+interface ParamIdValidatorProps {
+ element: React.ReactElement
+}
+
+export const ParamIdValidator = ({ element }: ParamIdValidatorProps) => {
+ const { formId, submissionId, paymentId } = useParams()
+
+ const isInvalidMongoId = (id?: string) => id && !id.match(MONGODB_ID_REGEX)
+
+ // Bootstrap route validation as suggested by React Router Docs:
+ // https://reactrouter.com/en/main/start/faq#what-happened-to-regexp-routes-paths
+ if (
+ isInvalidMongoId(formId) ||
+ isInvalidMongoId(submissionId) ||
+ isInvalidMongoId(paymentId)
+ )
+ return
+
+ return element
+}
diff --git a/frontend/src/constants/routes.ts b/frontend/src/constants/routes.ts
index 7c56f70e51..24e9ef6577 100644
--- a/frontend/src/constants/routes.ts
+++ b/frontend/src/constants/routes.ts
@@ -13,7 +13,7 @@ export const BILLING_ROUTE = '/billing'
// the regex in PublicFormPage.
export const PUBLICFORM_ROUTE = '/:formId'
export const USE_TEMPLATE_REDIRECT_SUBROUTE = 'use-template'
-export const FORMID_REGEX = /^([a-fA-F0-9]{24})$/
+export const MONGODB_ID_REGEX = /^([a-fA-F0-9]{24})$/
export const ADMINFORM_ROUTE = '/admin/form'
/** Build tab has no subroute, its the index admin form route. */
diff --git a/frontend/src/features/admin-form/common/queries.ts b/frontend/src/features/admin-form/common/queries.ts
index fb0db5c058..37d638b91f 100644
--- a/frontend/src/features/admin-form/common/queries.ts
+++ b/frontend/src/features/admin-form/common/queries.ts
@@ -6,7 +6,7 @@ import { AdminFormDto, PreviewFormViewDto } from '~shared/types/form/form'
import { ApiError } from '~typings/core'
-import { FORMID_REGEX } from '~constants/routes'
+import { MONGODB_ID_REGEX } from '~constants/routes'
import { useUser } from '~features/user/queries'
@@ -134,7 +134,7 @@ export const usePreviewForm = (
{
// Treat preview form as static on load.
staleTime: Infinity,
- enabled: FORMID_REGEX.test(formId) && enabled,
+ enabled: MONGODB_ID_REGEX.test(formId) && enabled,
},
)
}
@@ -150,7 +150,7 @@ export const useFormTemplate = (
{
// Treat preview form as static on load.
staleTime: Infinity,
- enabled: FORMID_REGEX.test(formId) && enabled,
+ enabled: MONGODB_ID_REGEX.test(formId) && enabled,
},
)
}
diff --git a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditMyInfoChildren/EditMyInfoChildren.tsx b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditMyInfoChildren/EditMyInfoChildren.tsx
index 22e674c320..9706cb9a69 100644
--- a/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditMyInfoChildren/EditMyInfoChildren.tsx
+++ b/frontend/src/features/admin-form/create/builder-and-design/BuilderAndDesignDrawer/EditFieldDrawer/edit-fieldtype/EditMyInfoChildren/EditMyInfoChildren.tsx
@@ -97,9 +97,7 @@ export const EditMyInfoChildren = ({
-
- Collect the following child information
-
+ Collect the following child data
= new Set([
+ MyInfoAttribute.Name,
+ MyInfoAttribute.DateOfBirth,
+ MyInfoAttribute.PassportNumber,
+ MyInfoAttribute.PassportExpiryDate,
+ // This is disabled due to MyInfo and sgID-MyInfo not using the same
+ // phone number formats.
+ // MyInfo phone numbers support country code, while sgID-MyInfo does not.
+ // MyInfoAttribute.MobileNo,
+
+ // FRM-1189: This is disabled due to slight different formatting.
+ // We format the Myinfo response by separates lines in addresses with comma
+ // Whereas sgID separates each line with newline.
+ // This should be enabled in future work
+ // MyInfoAttribute.RegisteredAddress,
+])
+
+/**
+ * If sgID is used, checks if the corresponding
+ * MyInfo field is supported by sgID.
+ */
+const sgIDUnSupported = (
+ form: AdminFormDto | undefined,
+ fieldType: MyInfoAttribute,
+): boolean =>
+ form?.authType === FormAuthType.SGID_MyInfo && !SGID_SUPPORTED.has(fieldType)
+
export const MyInfoFieldPanel = () => {
const { data: form, isLoading } = useCreateTabForm()
// myInfo should be disabled if
@@ -38,12 +70,21 @@ export const MyInfoFieldPanel = () => {
// 3. # of myInfo fields >= 30
const isMyInfoDisabled = useMemo(
() =>
- form?.responseMode !== FormResponseMode.Email ||
- form?.authType !== FormAuthType.MyInfo ||
- (form ? form.form_fields.filter(isMyInfo).length >= 30 : true),
+ form
+ ? form.form_fields.filter(isMyInfo).length >= 30 ||
+ form.responseMode !== FormResponseMode.Email ||
+ (form.authType !== FormAuthType.MyInfo &&
+ form.authType !== FormAuthType.SGID_MyInfo)
+ : true,
[form],
)
const isDisabled = isMyInfoDisabled || isLoading
+ const isDisabledCheck = useCallback(
+ (fieldType: MyInfoAttribute): boolean => {
+ return isDisabled || sgIDUnSupported(form, fieldType)
+ },
+ [form, isDisabled],
+ )
const { user } = useUser()
return (
@@ -56,7 +97,7 @@ export const MyInfoFieldPanel = () => {
{CREATE_MYINFO_PERSONAL_FIELDS_ORDERED.map((fieldType, index) => (
@@ -73,7 +114,7 @@ export const MyInfoFieldPanel = () => {
{CREATE_MYINFO_CONTACT_FIELDS_ORDERED.map((fieldType, index) => (
@@ -91,7 +132,7 @@ export const MyInfoFieldPanel = () => {
(fieldType, index) => (
@@ -109,7 +150,7 @@ export const MyInfoFieldPanel = () => {
{CREATE_MYINFO_MARRIAGE_FIELDS_ORDERED.map((fieldType, index) => (
@@ -128,7 +169,7 @@ export const MyInfoFieldPanel = () => {
(fieldType, index) => (
@@ -154,7 +195,8 @@ const MyInfoText = ({
responseMode,
form_fields,
}: MyInfoTextProps): JSX.Element => {
- const isMyInfoDisabled = authType !== FormAuthType.MyInfo
+ const isMyInfoDisabled =
+ authType !== FormAuthType.MyInfo && authType !== FormAuthType.SGID_MyInfo
const numMyInfoFields = useMemo(
() => form_fields.filter((ff) => isMyInfo(ff)).length,
[form_fields],
@@ -178,7 +220,10 @@ const MyInfoText = ({
return (
- {`Only 30 MyInfo fields are allowed in Email mode (${numMyInfoFields}/30).`}{' '}
+ {authType === FormAuthType.SGID_MyInfo
+ ? 'Some MyInfo fields are not yet supported in your selected authentication type. '
+ : null}
+ {`Only 30 MyInfo fields are allowed in Email mode (${numMyInfoFields}/30). `}
Learn more
diff --git a/frontend/src/features/admin-form/create/builder-and-design/constants.ts b/frontend/src/features/admin-form/create/builder-and-design/constants.ts
index 8d72f19b21..82dc078800 100644
--- a/frontend/src/features/admin-form/create/builder-and-design/constants.ts
+++ b/frontend/src/features/admin-form/create/builder-and-design/constants.ts
@@ -141,6 +141,9 @@ export const CREATE_MYINFO_CHILDREN_SUBFIELDS_OPTIONS: {
label: string
}[] = Object.values(MyInfoChildAttributes)
.filter((e) => e !== MyInfoChildAttributes.ChildName)
+ // TODO awaiting approval from MyInfo to get child vaccination status.
+ // Disabling in the frontend for now.
+ .filter((e) => e !== MyInfoChildAttributes.ChildVaxxStatus)
.map((value) => {
return {
value,
diff --git a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/AuthSettingsSection.tsx b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/AuthSettingsSection.tsx
index f90c3b20f7..f824086b98 100644
--- a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/AuthSettingsSection.tsx
+++ b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/AuthSettingsSection.tsx
@@ -6,28 +6,22 @@ import {
useMemo,
useState,
} from 'react'
-import { Box, Flex, Icon, Skeleton } from '@chakra-ui/react'
+import { Box, Flex, Skeleton, Spacer, Text } from '@chakra-ui/react'
import { FormAuthType, FormSettings, FormStatus } from '~shared/types/form'
-import { BxsHelpCircle } from '~assets/icons/BxsHelpCircle'
-import { OGP_SGID } from '~constants/links'
+import { GUIDE_SPCP_ESRVCID } from '~constants/links'
import InlineMessage from '~components/InlineMessage'
import Link from '~components/Link'
import Radio from '~components/Radio'
-import Tooltip from '~components/Tooltip'
+import { Tag } from '~components/Tag'
import { useAdminForm } from '~features/admin-form/common/queries'
import { isMyInfo } from '~features/myinfo/utils'
import { useMutateFormSettings } from '../../mutations'
-import {
- AUTHTYPE_TO_TEXT,
- CP_TOOLTIP,
- SGID_TOOLTIP,
- STORAGE_MODE_AUTHTYPES,
-} from './constants'
+import { AUTHTYPE_TO_TEXT, STORAGE_MODE_AUTHTYPES } from './constants'
import { EsrvcIdBox } from './EsrvcIdBox'
const esrvcidRequired = (authType: FormAuthType) => {
@@ -131,14 +125,31 @@ export const AuthSettingsSection = ({
return (
+
+ Authenticate respondents by NRIC.{' '}
+ e.stopPropagation()}
+ >
+ Learn more about Singpass authentication
+
+
{isFormPublic ? (
-
+
To change authentication method, close your form to new responses.
) : containsMyInfoFields ? (
-
- Authentication method cannot be changed without first removing MyInfo
- fields. You can still update your e-service ID.
+
+ To change authentication method, remove existing Myinfo fields on your
+ form. You can still update your e-service ID.
) : null}
-
+
{text}
- {authType === FormAuthType.SGID ? (
+ {authType === FormAuthType.SGID ||
+ authType === FormAuthType.SGID_MyInfo ? (
<>
-
-
-
- e.stopPropagation()}
- >
- Contact us to find out more
-
+
+
+ Free
+
>
) : null}
- {authType === FormAuthType.CP ? (
-
-
-
- ) : null}
diff --git a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/EsrvcIdBox.tsx b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/EsrvcIdBox.tsx
index 7052a1d8f2..c7bf034e6f 100644
--- a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/EsrvcIdBox.tsx
+++ b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/EsrvcIdBox.tsx
@@ -7,16 +7,14 @@ import {
InputGroup,
InputRightElement,
Stack,
+ Text,
VisuallyHidden,
} from '@chakra-ui/react'
import { FormAuthType, FormSettings } from '~shared/types/form'
-import { GUIDE_SPCP_ESRVCID } from '~constants/links'
-import { useMdComponents } from '~hooks/useMdComponents'
import FormErrorMessage from '~components/FormControl/FormErrorMessage'
import Input from '~components/Input'
-import { MarkdownText } from '~components/MarkdownText'
import Spinner from '~components/Spinner'
import { useMutateFormSettings } from '../../mutations'
@@ -47,15 +45,6 @@ export const EsrvcIdBox = ({
mode: 'onChange',
})
- const mdComponents = useMdComponents({
- styles: {
- text: {
- textStyle: 'body-2',
- color: 'secondary.400',
- },
- },
- })
-
const onSubmit = handleSubmit(({ esrvcId }) => {
if (esrvcId.trim() === initialEsrvcId) return
return mutateFormEsrvcId.mutate(esrvcId.trim(), {
@@ -74,22 +63,27 @@ export const EsrvcIdBox = ({
const renderedHelperText = useMemo(() => {
switch (settings.authType) {
case FormAuthType.SP:
- return `Find out [how to get your Singpass e-service ID](${GUIDE_SPCP_ESRVCID}).`
case FormAuthType.CP:
- return `Corppass now uses Singpass to authenticate corporate users. You will still need a separate **Corppass e-service ID**. Find out [how to get your Corppass e-service ID](${GUIDE_SPCP_ESRVCID}).`
case FormAuthType.MyInfo:
- return `Find out [how to get your MyInfo e-service ID](${GUIDE_SPCP_ESRVCID}).`
+ return 'Contact spcp.transoffice@accenture.com for your e-service ID'
default:
return ''
}
}, [settings.authType])
+ const placeHolder = useMemo(
+ () =>
+ `Enter ${
+ settings.authType === FormAuthType.CP ? 'Corppass' : 'Singpass'
+ } e-service ID`,
+ [settings.authType],
+ )
return (
e-service ID:
@@ -111,7 +105,7 @@ export const EsrvcIdBox = ({
})}
isDisabled={isDisabled}
isReadOnly={mutateFormEsrvcId.isLoading}
- placeholder="Enter Singpass e-service ID"
+ placeholder={placeHolder}
/>
{errors.esrvcId?.message}
diff --git a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/constants.ts b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/constants.ts
index 220a6f3300..6a4357d815 100644
--- a/frontend/src/features/admin-form/settings/components/AuthSettingsSection/constants.ts
+++ b/frontend/src/features/admin-form/settings/components/AuthSettingsSection/constants.ts
@@ -9,29 +9,21 @@ export type StorageFormAuthType =
export const STORAGE_MODE_AUTHTYPES: Record = {
[FormAuthType.NIL]: 'None',
+ [FormAuthType.SGID]: 'Singpass App-only Login',
[FormAuthType.SP]: 'Singpass',
- [FormAuthType.SGID]: 'Singpass App-only Login (Free)',
[FormAuthType.CP]: 'Singpass (Corporate)',
}
// Not using STORAGE_MODE_AUTHTYPES due to wanting a different order.
export const EMAIL_MODE_AUTHTYPES: Record = {
[FormAuthType.NIL]: 'None',
+ [FormAuthType.SGID]: 'Singpass App-only Login',
+ [FormAuthType.SGID_MyInfo]: 'Singpass App-only with Myinfo',
[FormAuthType.SP]: 'Singpass',
- [FormAuthType.SGID]: 'Singpass App-only Login (Free)',
- [FormAuthType.MyInfo]: 'Singpass with MyInfo',
+ [FormAuthType.MyInfo]: 'Singpass with Myinfo',
[FormAuthType.CP]: 'Singpass (Corporate)',
}
export const AUTHTYPE_TO_TEXT = {
[FormResponseMode.Email]: EMAIL_MODE_AUTHTYPES,
[FormResponseMode.Encrypt]: STORAGE_MODE_AUTHTYPES,
}
-
-export const SGID_TOOLTIP = `Free Singpass authentication via Singpass app QR\
- code login. Respondents must have the Singpass mobile app installed to log in\
- and submit responses. Password login is not supported. Form admin will receive\
- respondent's NRIC.`
-
-export const CP_TOOLTIP = `Corppass no longer has its own login page, and now\
- uses Singpass to authenticate corporate users. You will still need a separate\
- Corppass e-service ID.`
diff --git a/frontend/src/features/logic/utils/typeguards.ts b/frontend/src/features/logic/utils/typeguards.ts
index afda47e954..7bd39d5429 100644
--- a/frontend/src/features/logic/utils/typeguards.ts
+++ b/frontend/src/features/logic/utils/typeguards.ts
@@ -45,6 +45,6 @@ export const isLogicableField = (args: {
export const isValueStringArray = (
value: FormCondition['value'],
): value is string[] => {
- // use .some because of limitation of typecript in calling .every() on union of array types: https://github.com/microsoft/TypeScript/issues/44373
+ // use .some because of limitation of typescript in calling .every() on union of array types: https://github.com/microsoft/TypeScript/issues/44373
return Array.isArray(value) && !value.some((v) => typeof v === 'number')
}
diff --git a/frontend/src/features/public-form/PublicFormProvider.tsx b/frontend/src/features/public-form/PublicFormProvider.tsx
index 4ec473826a..1e0a06d7b1 100644
--- a/frontend/src/features/public-form/PublicFormProvider.tsx
+++ b/frontend/src/features/public-form/PublicFormProvider.tsx
@@ -28,7 +28,7 @@ import {
PublicFormDto,
} from '~shared/types/form'
-import { FORMID_REGEX } from '~constants/routes'
+import { MONGODB_ID_REGEX } from '~constants/routes'
import { useBrowserStm } from '~hooks/payments'
import { useTimeout } from '~hooks/useTimeout'
import { useToast } from '~hooks/useToast'
@@ -93,7 +93,7 @@ export function useCommonFormProvider(formId: string) {
return vfnTransaction.transactionId
}, [createTransactionMutation, vfnTransaction])
- const isNotFormId = useMemo(() => !FORMID_REGEX.test(formId), [formId])
+ const isNotFormId = useMemo(() => !MONGODB_ID_REGEX.test(formId), [formId])
const expiryInMs = useMemo(() => {
if (!vfnTransaction?.expireAt) return null
@@ -135,9 +135,12 @@ export const PublicFormProvider = ({
}
}, [submissionData])
+ // Only load catpcha if enabled on form and the user is not on GSIB
+ const enableCaptcha = data && data.form.hasCaptcha && !data.isIntranetUser
+
const {
data: { captchaPublicKey, turnstileSiteKey, useFetchForSubmissions } = {},
- } = useEnv(/* enabled= */ !!data?.form.hasCaptcha)
+ } = useEnv(/* enabled= */ enableCaptcha)
// Feature flag to control turnstile captcha rollout
// defaults to false
@@ -156,7 +159,7 @@ export const PublicFormProvider = ({
getTurnstileResponse,
containerID: turnstileContainerID,
} = useTurnstile({
- sitekey: data?.form.hasCaptcha ? turnstileSiteKey : undefined,
+ sitekey: enableCaptcha ? turnstileSiteKey : undefined,
enableUsage: enableTurnstileFeatureFlag,
})
@@ -165,7 +168,7 @@ export const PublicFormProvider = ({
getCaptchaResponse,
containerId: recaptchaContainerID,
} = useRecaptcha({
- sitekey: data?.form.hasCaptcha ? captchaPublicKey : undefined,
+ sitekey: enableCaptcha ? captchaPublicKey : undefined,
enableUsage: !enableTurnstileFeatureFlag,
})
@@ -592,7 +595,7 @@ export const PublicFormProvider = ({
isAuthRequired,
captchaContainerId: containerID,
expiryInMs,
- isLoading: isLoading || (!!data?.form.hasCaptcha && !hasLoaded),
+ isLoading: isLoading || (!!enableCaptcha && !hasLoaded),
isPaymentEnabled,
isPreview: false,
setNumVisibleFields,
diff --git a/frontend/src/features/public-form/components/FormAuth/FormAuth.tsx b/frontend/src/features/public-form/components/FormAuth/FormAuth.tsx
index 4bd3f32c30..c495abd56d 100644
--- a/frontend/src/features/public-form/components/FormAuth/FormAuth.tsx
+++ b/frontend/src/features/public-form/components/FormAuth/FormAuth.tsx
@@ -42,6 +42,7 @@ export const FormAuth = ({ authType }: FormAuthProps): JSX.Element => {
'Corporate entity login is required for this form.\nYour Singpass ID and corporate Entity ID will be included with your form submission.',
}
case FormAuthType.SGID:
+ case FormAuthType.SGID_MyInfo:
return {
authType: 'Singpass app',
helpText:
diff --git a/frontend/src/features/public-form/queries.ts b/frontend/src/features/public-form/queries.ts
index c246ce9f43..c6f5bb2dc3 100644
--- a/frontend/src/features/public-form/queries.ts
+++ b/frontend/src/features/public-form/queries.ts
@@ -4,7 +4,7 @@ import { PublicFormViewDto } from '~shared/types/form/form'
import { ApiError } from '~typings/core'
-import { FORMID_REGEX } from '~constants/routes'
+import { MONGODB_ID_REGEX } from '~constants/routes'
import { getPublicFormView } from './PublicFormService'
@@ -26,7 +26,7 @@ export const usePublicFormView = (
{
// Treat form as static on load.
staleTime: Infinity,
- enabled: FORMID_REGEX.test(formId) && enabled,
+ enabled: MONGODB_ID_REGEX.test(formId) && enabled,
},
)
}
diff --git a/frontend/src/hooks/useLocalStorage.ts b/frontend/src/hooks/useLocalStorage.ts
index a4826d6d1c..b9cc37ae94 100644
--- a/frontend/src/hooks/useLocalStorage.ts
+++ b/frontend/src/hooks/useLocalStorage.ts
@@ -11,7 +11,7 @@ export const useLocalStorage = (
// Get from local storage then
// parse stored json or return initialValue
const readValue = useCallback(() => {
- // Prevent build error "window is undefined" but keep keep working
+ // Prevent build error "window is undefined" and keep it working
if (typeof window === 'undefined') {
return initialValue
}
diff --git a/frontend/src/hooks/useMdComponents.tsx b/frontend/src/hooks/useMdComponents.tsx
index de5a09e06c..35f515cdfd 100644
--- a/frontend/src/hooks/useMdComponents.tsx
+++ b/frontend/src/hooks/useMdComponents.tsx
@@ -84,7 +84,7 @@ export const useMdComponents = ({
a: ({ node, ...props }) => {
const { href } = props
const isExternal =
- typeof href === 'string' && !href.startsWith(window.location.origin)
+ (href && !href.startsWith(window.location.origin)) || false
return
},
diff --git a/frontend/src/templates/Field/ChildrenCompound/ChildrenCompoundField.tsx b/frontend/src/templates/Field/ChildrenCompound/ChildrenCompoundField.tsx
index e331d9cd8e..b7a195d8ea 100644
--- a/frontend/src/templates/Field/ChildrenCompound/ChildrenCompoundField.tsx
+++ b/frontend/src/templates/Field/ChildrenCompound/ChildrenCompoundField.tsx
@@ -124,24 +124,27 @@ export const ChildrenCompoundField = ({
aria-describedby={`children-desc-${schema._id}`}
aria-labelledby={`${schema._id}-label`}
>
-
- {fields.map((field, currChildBodyIdx) => (
-
- ))}
-
+ <>
+
+
+ {fields.map((field, currChildBodyIdx) => (
+
+ ))}
+
+ >
{schema.allowMultiple ? (