Skip to content

Commit

Permalink
Merge pull request #6623 from opengovsg/release_v6.70.0
Browse files Browse the repository at this point in the history
* fix(deps): bump libphonenumber-js from 1.10.38 to 1.10.39 in /shared (#6594)

Bumps [libphonenumber-js](https://gitlab.com/catamphetamine/libphonenumber-js) from 1.10.38 to 1.10.39.
- [Changelog](https://gitlab.com/catamphetamine/libphonenumber-js/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/catamphetamine/libphonenumber-js/compare/v1.10.38...v1.10.39)

---
updated-dependencies:
- dependency-name: libphonenumber-js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore: remove customMin and customMax virtuals (#6596)

Removed from number field and text field schemas

* chore: remove angular deps (#6602)

* fix(deps): bump validator from 13.9.0 to 13.11.0 in /shared (#6605)

Bumps [validator](https://github.com/validatorjs/validator.js) from 13.9.0 to 13.11.0.
- [Release notes](https://github.com/validatorjs/validator.js/releases)
- [Changelog](https://github.com/validatorjs/validator.js/blob/master/CHANGELOG.md)
- [Commits](validatorjs/validator.js@13.9.0...13.11.0)

---
updated-dependencies:
- dependency-name: validator
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* docs: update feature section of README (#6603)

* docs: update feature section of README

* docs: remove E2E wording for encryption

* fix(deps): bump import-in-the-middle from 1.3.4 to 1.4.2 (#6609)

Bumps [import-in-the-middle](https://github.com/DataDog/import-in-the-middle) from 1.3.4 to 1.4.2.
- [Release notes](https://github.com/DataDog/import-in-the-middle/releases)
- [Commits](nodejs/import-in-the-middle@v1.3.4...v1.4.2)

---
updated-dependencies:
- dependency-name: import-in-the-middle
  dependency-type: indirect
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(deps): bump type-fest from 4.1.0 to 4.2.0 in /shared (#6610)

Bumps [type-fest](https://github.com/sindresorhus/type-fest) from 4.1.0 to 4.2.0.
- [Release notes](https://github.com/sindresorhus/type-fest/releases)
- [Commits](sindresorhus/type-fest@v4.1.0...v4.2.0)

---
updated-dependencies:
- dependency-name: type-fest
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* chore(deps-dev): bump @types/lodash from 4.14.196 to 4.14.197 in /shared (#6612)

Bumps [@types/lodash](https://github.com/DefinitelyTyped/DefinitelyTyped/tree/HEAD/types/lodash) from 4.14.196 to 4.14.197.
- [Release notes](https://github.com/DefinitelyTyped/DefinitelyTyped/releases)
- [Commits](https://github.com/DefinitelyTyped/DefinitelyTyped/commits/HEAD/types/lodash)

---
updated-dependencies:
- dependency-name: "@types/lodash"
  dependency-type: direct:development
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat: filter out API details from user object (#6588)

* feat: restrict user fields to email and agency

* feat: remove apiToken property from populated user

* test: return correct admin properties in tests

* feat: remove apiToken from user queries

* feat: filter out apiToken in user model

* test: revert changes made to tests

* fix: remove line breaks

* fix: refine mongoose setup

* test: add check for apiToken property

* chore: Added react dev inspector (#6611)

* added react dev inspector

* moved react dev inspector to dev dependency

* changed command to trigger inspector

* fix(deps): bump libphonenumber-js from 1.10.39 to 1.10.40 in /shared (#6614)

Bumps [libphonenumber-js](https://gitlab.com/catamphetamine/libphonenumber-js) from 1.10.39 to 1.10.40.
- [Changelog](https://gitlab.com/catamphetamine/libphonenumber-js/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/catamphetamine/libphonenumber-js/compare/v1.10.39...v1.10.40)

---
updated-dependencies:
- dependency-name: libphonenumber-js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* fix(deps): bump libphonenumber-js from 1.10.40 to 1.10.41 in /shared (#6616)

Bumps [libphonenumber-js](https://gitlab.com/catamphetamine/libphonenumber-js) from 1.10.40 to 1.10.41.
- [Changelog](https://gitlab.com/catamphetamine/libphonenumber-js/blob/master/CHANGELOG.md)
- [Commits](https://gitlab.com/catamphetamine/libphonenumber-js/compare/v1.10.40...v1.10.41)

---
updated-dependencies:
- dependency-name: libphonenumber-js
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>

* feat: payment by products (#6301)

* wip

* refactor: move admin payments components to PaymentPanel folder

* add AddProductModal and basic item creation flow

* add BE payments product

* wip

* refactor admin payment products input

* fix version not passed to BE

* add _id to be exposed in product model, refactor product types

* wip add responder payment

* feat: add price calculation

* add clear variant for SingleSelect

* add ProductItem Quantity display

* add full width variant for checkbox

* refactor typing, add submit action to pass payment products

* add missing field id constant

* add registering of payment products into form context

* refactor: extract stripe events fn to stripe.events.controller

* refactor: break submission controller into its payment/non-payment creation handlers

* refactor: extract checks into ensure pipelines

* fix: remove duplicate PaymentItemDetailBlock

* fix: next() to be awaited

* refactor: extract price calculation to shared, rename ensuresIsX to ensureX

* fix: hide payment items block for v2, update preview to render using version specific

* refactor: submitEncryptModeForm

* refactor: FieldListDrawer with mapped header+component

* feat: add edit product

* feat: changed ProductModal min/max qty input to hide when multiqty is disabled

* fix: clicking PaymentPreview should redirect to their respective version of payment tab

* fix: paymentpreview to update with latest products when paymentstore changes

* feat: add product deletion flow for admin

* fix: Payment v2 description, refactor PaymetnItemDetailsBlock

* refactor: cleanup #1

* fix: edit mode multi_qty to reference incoming value, fix qty range generation

* fix: merge conflicts, to render-able state

* fix: be to compile-able state

* chore: revert unintended changes

* wip

* fix: remove unnecessary payment checks in handle update payment product

* fix(fe): adding products triggering local data replacement

* feat: support non-multi selection

* fix: quantity selection does not auto select item

* chore: remove unused comments

* feat: add full width variant on radio component

* chore: camelcase checkbox component theme

* feat: store purchased products into payment doc

* chore: remove unused imports

* fix: payment product quantity converted to boolean instead of number

* fix: test cases failing due to incorrect object comparison

* fix: duplicated code from incorrect merge
 resolution

* chore: update payment page width to match design

* feat: expose products to Payment Page

* refactor: payment UI

* feat: split payments summaries for fixed, variable, and products

* feat: add full payment summary for products

* fix: typing issues

* feat: itemized invoice for payment by products (#6574)

* fix: test cases failing due to incorrect object comparison

* fix: add products button not disabled when panel is disabled

* fix: products payment not showing title on paymentpreview

* feat: add product qty validation

* fix: remove stray test capture group

* feat: show error message if no products are selected (#6585)

* feat: add error message if no product is selected

* fix: change copy

* feat: set default quantity as min qty

* feat: use isProductSelected function to check if at least 1 product is selected

* fix: use Array.prototype.some()

* feat: hide fixed payment type if form is not a fixed payment

* fix: addproduct modal not displaying errors

* chore: send log all payments info instead of only products

* chore: remove unused files

* fix: add product not validating if max qty-payment amount exceed global limits

* chore: use divider instead of hr

* chore: add strong joi validator

* chore: update copy for payment qty-payment amount exceed

* refactor: change function into class to better express side-effects

* refactor: rename productitemschema to productschema

* feat: add joi validation for handleupdatepaymentsproduct

* chore: fix typo, remove unused comments nits

* feat: add payment summary to thank you page (#6591)

* feat: add CompletedPaymentSummary

* feat: add payment_fields_snapshot to payment model

* feat: show products and other payment metadata in thank you page

* feat: db migration script for payment_fields_snapshot

* fix: rename ProductItemSchema to ProductSchema

* fix: use form payment_fields as source of truth in payments model

* ref: refactor getProductNames

* ref: remove unused code

* test: update FormPaymentPage.stories

* refactor: paymentproducts to share same joi validator, add validator to encrypt-submission

* chore: copy changes, tweak order of payment type dropdown, fix padding when payment is not connected

* chore: remove title for payments by product type

* feat: add payment preview placeholder when admin has no items

* chore: products description to be optional, ui changes on payment preview product item

* feat: auto detect disabling multi product toggle

* chore: update copy for payment summary

* fix: unstuck divider with productitem on paymentpreview

* chore: product modal to calculate qty, ui updates

* fix: update text colors

* fix: expand button to full width for mobile

* fix: adjust spacing before recaptcha container

* fix: new payment form not defaulting to products

* fix: product payment type should not require name field validated

* fix: remove mention of gst on product modal if form is not gst enabled

* fix: update test cases to reflect new defaults

* fix: payment date race condition (#6619)

fix: move payment date to be returned together when receipt url exists

---------

Co-authored-by: wanlingt <56983748+wanlingt@users.noreply.github.com>
Co-authored-by: wanlingt <wanling@open.gov.sg>

* fix: supply empty object when snapshot script has not completely migrated (#6622)

* chore: bump version to v6.70.0

---------

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Foo Chi Fa <59867455+foochifa@users.noreply.github.com>
Co-authored-by: LeonardYam <yamthesmall@gmail.com>
Co-authored-by: Justyn Oh <justynoh@gmail.com>
Co-authored-by: sebastianwzq <136435307+sebastianwzq@users.noreply.github.com>
Co-authored-by: Ken Lee Shu Ming <ken@open.gov.sg>
  • Loading branch information
7 people authored Aug 16, 2023
2 parents 9a4ae0d + 1b4c82c commit 479d6f6
Show file tree
Hide file tree
Showing 89 changed files with 4,639 additions and 1,490 deletions.
109 changes: 59 additions & 50 deletions CHANGELOG.md

Large diffs are not rendered by default.

11 changes: 2 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,20 +48,13 @@ Notable features include:
- 19 different form field types, including attachments, tables, email and mobile
- Verified email and mobile phone fields via integrations with Twilio and AWS SES
- Automatic emailing of submissions for forms built with Email Mode
- End-to-end encryption for forms built with Storage Mode
- Encryption for data collected on forms built with Storage Mode
- (Singapore government agencies only) Citizen authentication with [SingPass](https://www.singpass.gov.sg/singpass/common/aboutus)
- (Singapore government agencies only) Citizen authentication with [sgID](https://www.id.gov.sg/)
- (Singapore government agencies only) Corporate authentication with [CorpPass](https://www.corppass.gov.sg/corppass/common/aboutus)
- (Singapore government agencies only) Automatic prefill of verified data with [MyInfo](https://www.singpass.gov.sg/myinfo/common/aboutus)
- Webhooks functionality via the official [FormSG JavaScript SDK](https://github.com/opengovsg/formsg-sdk) and contributor-supported [FormSG Ruby SDK](https://github.com/opengovsg/formsg-ruby-sdk)

The current product roadmap includes:

- (in progress) Frontend rewrite from [AngularJS](https://angularjs.org/) to [React](https://reactjs.org/)
- Enabling payments on forms
- Electronic signatures
- Notifications to form admins for Storage mode submissions
- Integration with vault.gov.sg
- Variable amount and Itemised payments on forms with [stripe](https://stripe.com) integration

## Local Development (Docker)

Expand Down
7 changes: 7 additions & 0 deletions __tests__/unit/backend/helpers/jest-db.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ import {
IPopulatedForm,
ISubmissionSchema,
IUserSchema,
UserApiToken,
} from 'src/types'

/**
Expand Down Expand Up @@ -88,18 +89,21 @@ const insertUser = async ({
userId,
mailDomain = 'test.gov.sg',
mailName = 'test',
apiToken,
}: {
agencyId: ObjectID
userId?: ObjectID
mailName?: string
mailDomain?: string
apiToken?: UserApiToken
}): Promise<IUserSchema> => {
const User = getUserModel(mongoose)

return User.create({
email: `${mailName}@${mailDomain}`,
_id: userId,
agency: agencyId,
apiToken: apiToken,
})
}

Expand All @@ -116,13 +120,15 @@ const insertFormCollectionReqs = async ({
shortName = 'govtest',
flags,
betaFlags,
apiToken,
}: {
userId?: ObjectID
mailName?: string
mailDomain?: string
shortName?: string
flags?: { lastSeenFeatureUpdateVersion: number }
betaFlags?: IUserSchema['betaFlags']
apiToken?: UserApiToken
} = {}): Promise<{
agency: AgencyDocument
user: IUserSchema
Expand All @@ -137,6 +143,7 @@ const insertFormCollectionReqs = async ({
agency: agency._id,
flags,
betaFlags,
apiToken,
})

return { agency, user }
Expand Down
4 changes: 2 additions & 2 deletions frontend/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion frontend/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "form-frontend",
"version": "6.69.0",
"version": "6.70.0",
"homepage": ".",
"private": true,
"dependencies": {
Expand Down
54 changes: 39 additions & 15 deletions frontend/src/app/App.tsx
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import React from 'react'
import { Inspector, InspectParams } from 'react-dev-inspector'
import { HelmetProvider } from 'react-helmet-async'
import { QueryClient, QueryClientProvider } from 'react-query'
import { ReactQueryDevtools } from 'react-query/devtools'
Expand Down Expand Up @@ -40,18 +42,40 @@ datadogLogs.init({
sampleRate: 100,
})

export const App = (): JSX.Element => (
<HelmetProvider>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<AppHelmet />
<BrowserRouter>
<ChakraProvider theme={theme} resetCSS>
<AuthProvider>
<AppRouter />
</AuthProvider>
</ChakraProvider>
</BrowserRouter>
</QueryClientProvider>
</HelmetProvider>
)
export const App = (): JSX.Element => {
const isDev = process.env.NODE_ENV === 'development'

return (
<>
{isDev && (
<Inspector
// props see docs:
// https://github.com/zthxxx/react-dev-inspector#inspector-component-props
keys={['control', 'shift', 'c']}
disableLaunchEditor={true}
onClickElement={({ codeInfo }: InspectParams) => {
if (!codeInfo?.absolutePath) return
const { absolutePath, lineNumber, columnNumber } = codeInfo
// you can change the url protocol if you are using in Web IDE
window.open(
`vscode://file/${absolutePath}:${lineNumber}:${columnNumber}`,
)
}}
/>
)}
<HelmetProvider>
<QueryClientProvider client={queryClient}>
<ReactQueryDevtools initialIsOpen={false} />
<AppHelmet />
<BrowserRouter>
<ChakraProvider theme={theme} resetCSS>
<AuthProvider>
<AppRouter />
</AuthProvider>
</ChakraProvider>
</BrowserRouter>
</QueryClientProvider>
</HelmetProvider>
</>
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ export interface SingleSelectProviderProps<
children: React.ReactNode
/** Color scheme of component */
colorScheme?: ThemeColorScheme
/** Variant of component */
variant?: 'clear'
fullWidth?: boolean
}
export const SingleSelectProvider = ({
Expand All @@ -60,6 +62,7 @@ export const SingleSelectProvider = ({
inputAria,
colorScheme,
comboboxProps = {},
variant,
fullWidth = false,
}: SingleSelectProviderProps): JSX.Element => {
const { items, getItemByValue } = useItems({ rawItems })
Expand Down Expand Up @@ -222,6 +225,7 @@ export const SingleSelectProvider = ({
const styles = useMultiStyleConfig('SingleSelect', {
isClearable,
colorScheme,
variant,
})

const virtualListHeight = useMemo(() => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,7 @@ export const SelectCombobox = forwardRef<HTMLInputElement>(
align="center"
zIndex={2}
aria-hidden
sx={styles.inputStack}
>
{selectedItemMeta.icon ? (
<Icon
Expand Down
18 changes: 18 additions & 0 deletions frontend/src/features/admin-form/common/AdminFormPageService.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import {
EndPageUpdateDto,
PaymentsProductUpdateDto,
PaymentsUpdateDto,
StartPageUpdateDto,
} from '~shared/types'
Expand Down Expand Up @@ -58,3 +59,20 @@ export const updateFormPayments = async (
newPayments,
).then(({ data }) => data)
}

/**
* Updates the payments for the given form referenced by its id
*
* @param formId the id of the form to update payments for
* @param newPayments the new payment to replace with
* @returns the updated payment on success
*/
export const updateFormPaymentProducts = async (
formId: string,
products: PaymentsProductUpdateDto,
): Promise<PaymentsProductUpdateDto> => {
return ApiService.put<PaymentsProductUpdateDto>(
`${ADMIN_FORM_ENDPOINT}/${formId}/payments/products`,
products,
).then(({ data }) => data)
}
30 changes: 30 additions & 0 deletions frontend/src/features/admin-form/common/mutations.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
EndPageUpdateDto,
FormPermission,
FormPermissionsDto,
PaymentsProductUpdateDto,
PaymentsUpdateDto,
StartPageUpdateDto,
} from '~shared/types/form/form'
Expand Down Expand Up @@ -35,6 +36,7 @@ import { useCollaboratorWizard } from './components/CollaboratorModal/Collaborat
import { permissionsToRole } from './components/CollaboratorModal/utils'
import {
updateFormEndPage,
updateFormPaymentProducts,
updateFormPayments,
updateFormStartPage,
} from './AdminFormPageService'
Expand Down Expand Up @@ -422,10 +424,38 @@ export const useMutateFormPage = () => {
},
)

const paymentsProductMutation = useMutation(
(products: PaymentsProductUpdateDto) =>
updateFormPaymentProducts(formId, products),
{
onSuccess: (newData) => {
toast.closeAll()
queryClient.setQueryData<AdminStorageFormDto | undefined>(
adminFormKeys.products(formId, newData),
(oldData) =>
oldData
? {
...oldData,
payments_field: {
...oldData.payments_field,
products: newData,
},
}
: undefined,
)
toast({
description: 'Payments product was updated.',
})
},
onError: handleError,
},
)

return {
startPageMutation,
endPageMutation,
paymentsMutation,
paymentsProductMutation,
}
}

Expand Down
3 changes: 3 additions & 0 deletions frontend/src/features/admin-form/common/queries.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { useMemo } from 'react'
import { useQuery, UseQueryOptions, UseQueryResult } from 'react-query'
import { useParams } from 'react-router-dom'

import { Product } from '~shared/types'
import { AdminFormDto, PreviewFormViewDto } from '~shared/types/form/form'

import { ApiError } from '~typings/core'
Expand Down Expand Up @@ -29,6 +30,8 @@ export const adminFormKeys = {
[...adminFormKeys.id(id), 'previewForm'] as const,
viewFormTemplate: (id: string) =>
[...adminFormKeys.id(id), 'viewFormTemplate'] as const,
products: (id: string, products: Product[]) =>
[...adminFormKeys.id(id), 'products', ...products] as const,
}

/**
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import { useUser } from '~features/user/queries'

import { useCreateTabForm } from '../../../builder-and-design/useCreateTabForm'
import { CreatePageDrawerCloseButton } from '../../../common'
import { FieldListTabIndex } from '../../constants'

import {
BasicFieldPanel,
Expand All @@ -36,6 +37,30 @@ export const FieldListDrawer = (): JSX.Element => {
const displayPayments =
user?.betaFlags?.payment || flags?.has(featureFlags.payment)

const tabsDataList = [
{
header: 'Basic',
component: BasicFieldPanel,
isHidden: false,
isDisabled: isLoading,
key: FieldListTabIndex.Basic,
},
{
header: 'MyInfo',
component: MyInfoFieldPanel,
isHidden: false,
isDisabled: isLoading,
key: FieldListTabIndex.MyInfo,
},
{
header: 'Payments',
component: PaymentsInputPanel,
isHidden: !displayPayments,
isDisabled: isLoading,
key: FieldListTabIndex.Payments,
},
].filter((tab) => !tab.isHidden)

return (
<Tabs
pos="relative"
Expand All @@ -54,24 +79,20 @@ export const FieldListDrawer = (): JSX.Element => {
<CreatePageDrawerCloseButton />
</Flex>
<TabList mx="-0.25rem" w="100%">
<Tab isDisabled={isLoading}>Basic</Tab>
<Tab isDisabled={isLoading}>MyInfo</Tab>
{displayPayments && <Tab isDisabled={isLoading}>Payments</Tab>}
{tabsDataList.map((tab) => (
<Tab key={tab.key} isDisabled={tab.isDisabled}>
{tab.header}
</Tab>
))}
</TabList>
<Divider w="auto" mx="-1.5rem" />
</Box>
<TabPanels pb="1rem" flex={1} overflowY="auto">
<TabPanel>
<BasicFieldPanel />
</TabPanel>
<TabPanel>
<MyInfoFieldPanel />
</TabPanel>
{displayPayments && (
<TabPanel>
<PaymentsInputPanel />
{tabsDataList.map((tab) => (
<TabPanel key={tab.key}>
<tab.component />
</TabPanel>
)}
))}
</TabPanels>
</Tabs>
)
Expand Down
Loading

0 comments on commit 479d6f6

Please sign in to comment.