From 92d7a9903bc8d13222a66fb4e67daf36c97421d7 Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Thu, 9 Nov 2023 21:04:42 +0400 Subject: [PATCH] UIORGS-390 Implement base components and hooks for banking information (no logic) --- package.json | 1 + .../OrganizationCreate/OrganizationCreate.js | 15 ++- .../OrganizationEdit/OrganizationEdit.js | 27 ++++- .../BankingInformationField.js | 101 ++++++++++++++++++ .../BankingInformationField/index.js | 1 + .../OrganizationBankingInfoForm.js | 54 ++++++++++ .../OrganizationBankingInfoForm/index.js | 1 + .../OrganizationForm/OrganizationForm.js | 8 ++ src/Organizations/constants.js | 3 +- src/common/constants/api.js | 1 + src/common/hooks/index.js | 2 + .../hooks/useBankingAccountTypes/index.js | 1 + .../useBankingAccountTypes.js | 40 +++++++ .../index.js | 1 + .../useOrganizationBankingInformation.js | 45 ++++++++ translations/ui-organizations/en.json | 9 ++ 16 files changed, 301 insertions(+), 9 deletions(-) create mode 100644 src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/BankingInformationField.js create mode 100644 src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/index.js create mode 100644 src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.js create mode 100644 src/Organizations/OrganizationForm/OrganizationBankingInfoForm/index.js create mode 100644 src/common/hooks/useBankingAccountTypes/index.js create mode 100644 src/common/hooks/useBankingAccountTypes/useBankingAccountTypes.js create mode 100644 src/common/hooks/useOrganizationBankingInformation/index.js create mode 100644 src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js diff --git a/package.json b/package.json index 158442d4..9f34bfc1 100644 --- a/package.json +++ b/package.json @@ -19,6 +19,7 @@ "home": "/organizations", "okapiInterfaces": { "acquisition-methods": "1.0", + "banking-information": "1.0", "data-export-spring": "1.0", "configuration": "2.0", "organizations.organizations": "1.0", diff --git a/src/Organizations/OrganizationCreate/OrganizationCreate.js b/src/Organizations/OrganizationCreate/OrganizationCreate.js index 24f5d6de..4fd63c8f 100644 --- a/src/Organizations/OrganizationCreate/OrganizationCreate.js +++ b/src/Organizations/OrganizationCreate/OrganizationCreate.js @@ -1,4 +1,4 @@ -import React, { useCallback } from 'react'; +import { useCallback, useMemo } from 'react'; import PropTypes from 'prop-types'; import { withRouter } from 'react-router-dom'; import { useIntl } from 'react-intl'; @@ -46,7 +46,10 @@ export const OrganizationCreate = ({ history, location, mutator }) => { const showCallout = useShowCallout(); const intl = useIntl(); const createOrganization = useCallback( - (data) => { + ({ bankingInformation, ...data }) => { + // TODO: implement create banking info logic + console.log('bankingInformation on create', bankingInformation); + return mutator.createOrganizationOrg.POST(data) .then(organization => { setTimeout(() => cancelForm(organization.id)); @@ -63,9 +66,15 @@ export const OrganizationCreate = ({ history, location, mutator }) => { [cancelForm, intl, showCallout], ); + // TODO: provide info without perms and setting? + const initialValues = useMemo(() => ({ + bankingInformation: [], + ...INITIAL_VALUES, + }), []); + return ( diff --git a/src/Organizations/OrganizationEdit/OrganizationEdit.js b/src/Organizations/OrganizationEdit/OrganizationEdit.js index 326bef8a..538a334f 100644 --- a/src/Organizations/OrganizationEdit/OrganizationEdit.js +++ b/src/Organizations/OrganizationEdit/OrganizationEdit.js @@ -1,6 +1,7 @@ -import React, { +import { useCallback, useEffect, + useMemo, useState, } from 'react'; import { FormattedMessage, useIntl } from 'react-intl'; @@ -19,6 +20,7 @@ import { } from '@folio/stripes-acq-components'; import { VIEW_ORG_DETAILS } from '../../common/constants'; +import { useOrganizationBankingInformation } from '../../common/hooks'; import { organizationResourceByUrl } from '../../common/resources'; import { OrganizationForm, @@ -29,17 +31,22 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => { const organizationId = match.params.id; const [organization, setOrganization] = useState({}); - const [isLoading, setIsLoading] = useState(true); + const [isOrganizationLoading, setIsOrganizationLoading] = useState(true); const showCallout = useShowCallout(); const intl = useIntl(); + const { + bankingInformation: bankingInformationData, + isLoading: isBankingInformationLoading, + } = useOrganizationBankingInformation(organizationId); + useEffect( () => { mutator.editOrganizationOrg.GET() .then(organizationsResponse => { setOrganization(organizationsResponse); }) - .finally(() => setIsLoading(false)); + .finally(() => setIsOrganizationLoading(false)); }, // eslint-disable-next-line react-hooks/exhaustive-deps [], @@ -58,7 +65,10 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => { ); const updateOrganization = useCallback( - (data) => { + ({ bankingInformation, ...data }) => { + // TODO: implement create/update banking info logic + console.log('bankingInformation on update', bankingInformation); + return mutator.editOrganizationOrg.PUT(data) .then(() => { setTimeout(cancelForm); @@ -75,6 +85,13 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => { [cancelForm, intl, showCallout], ); + const initialValues = useMemo(() => ({ + bankingInformation: bankingInformationData, + ...organization, + }), [organization, bankingInformationData]); + + const isLoading = isOrganizationLoading || isBankingInformationLoading; + if (isLoading) { return ( @@ -85,7 +102,7 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => { return ( } diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/BankingInformationField.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/BankingInformationField.js new file mode 100644 index 00000000..375ad90a --- /dev/null +++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/BankingInformationField.js @@ -0,0 +1,101 @@ +import PropTypes from 'prop-types'; +import { Field } from 'react-final-form'; +import { FormattedMessage } from 'react-intl'; + +import { + Card, + Col, + Row, + TextField, +} from '@folio/stripes/components'; +import { FieldSelectionFinal } from '@folio/stripes-acq-components'; + +import { FieldIsPrimary } from '../../../../common/components'; + +export const BankingInformationField = ({ + bankingAccountTypeOptions, + fields, + index, + name, +}) => { + const cardHeader = ( + + ); + + return ( + + + + } + name={`${name}.bankName`} + component={TextField} + fullWidth + validateFields={[]} + /> + + + } + name={`${name}.bankAccountNumber`} + component={TextField} + fullWidth + validateFields={[]} + /> + + + } + name={`${name}.transitNumber`} + component={TextField} + fullWidth + validateFields={[]} + /> + + + } + name={`${name}.addressId`} + // TODO: refine address field + dataOptions={[]} + validateFields={[]} + /> + + + } + name={`${name}.accountTypeId`} + dataOptions={bankingAccountTypeOptions} + fullWidth + validateFields={[]} + /> + + + } + name={`${name}.notes`} + id={`${name}.notes`} + component={TextField} + fullWidth + validateFields={[]} + /> + + + + ); +}; + +BankingInformationField.propTypes = { + bankingAccountTypeOptions: PropTypes.arrayOf(PropTypes.shape({ + label: PropTypes.string, + value: PropTypes.string, + })).isRequired, + fields: PropTypes.object.isRequired, + index: PropTypes.number.isRequired, + name: PropTypes.string.isRequired, +}; diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/index.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/index.js new file mode 100644 index 00000000..70a06bd3 --- /dev/null +++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/BankingInformationField/index.js @@ -0,0 +1 @@ +export { BankingInformationField } from './BankingInformationField'; diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.js new file mode 100644 index 00000000..5b006783 --- /dev/null +++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.js @@ -0,0 +1,54 @@ +import { useMemo } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { FieldArray } from 'react-final-form-arrays'; + +import { Loading } from '@folio/stripes/components'; +import { RepeatableFieldWithValidation } from '@folio/stripes-acq-components'; + +import { useBankingAccountTypes } from '../../../common/hooks'; +import { + createAddNewItem, + removeItem, +} from '../../../common/utils'; +import { validatePrimary } from '../../../common/validation'; +import { BankingInformationField } from './BankingInformationField'; + +const renderField = (props) => (name, index, fields) => ( + +); + +export const OrganizationBankingInfoForm = () => { + const { + bankingAccountTypes, + isFetching, + } = useBankingAccountTypes(); + + const bankingAccountTypeOptions = useMemo(() => { + return bankingAccountTypes.map(({ id, name }) => ({ + label: name, + value: id, + })); + }, [bankingAccountTypes]); + + if (isFetching) { + return ; + } + + return ( + } + component={RepeatableFieldWithValidation} + id="bankingInformation" + name="bankingInformation" + onAdd={createAddNewItem()} + onRemove={removeItem} + renderField={renderField({ bankingAccountTypeOptions })} + validate={validatePrimary} + /> + ); +}; diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/index.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/index.js new file mode 100644 index 00000000..0538aaeb --- /dev/null +++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/index.js @@ -0,0 +1 @@ +export { OrganizationBankingInfoForm } from './OrganizationBankingInfoForm'; diff --git a/src/Organizations/OrganizationForm/OrganizationForm.js b/src/Organizations/OrganizationForm/OrganizationForm.js index e1d6bd4d..d36aacc6 100644 --- a/src/Organizations/OrganizationForm/OrganizationForm.js +++ b/src/Organizations/OrganizationForm/OrganizationForm.js @@ -24,6 +24,7 @@ import { } from '@folio/stripes-acq-components'; import { ORGANIZATIONS_ROUTE } from '../../common/constants'; +import { OrganizationBankingInfoForm } from './OrganizationBankingInfoForm'; import { OrganizationSummaryForm } from './OrganizationSummaryForm'; import { OrganizationContactInfoFormContainer } from './OrganizationContactInfoForm'; import { OrganizationContactPeopleForm } from './OrganizationContactPeopleForm'; @@ -200,6 +201,13 @@ const OrganizationForm = ({ + + + + , + [ORGANIZATION_SECTIONS.bankingInformationSection]: , [ORGANIZATION_SECTIONS.contactInformationSection]: , [ORGANIZATION_SECTIONS.contactPeopleSection]: , [ORGANIZATION_SECTIONS.interfacesSection]: , diff --git a/src/common/constants/api.js b/src/common/constants/api.js index 5d7d559a..a9055baa 100644 --- a/src/common/constants/api.js +++ b/src/common/constants/api.js @@ -1,5 +1,6 @@ export const AGREEMENTS_API = 'erm/sas'; export const BANKING_ACCOUNT_TYPES_API = 'organizations-storage/banking-account-types'; +export const BANKING_INFORMATION_API = 'organizations/banking-information'; export const CATEGORIES_API = 'organizations-storage/categories'; export const CONTACTS_API = 'organizations-storage/contacts'; export const INTERFACES_API = 'organizations-storage/interfaces'; diff --git a/src/common/hooks/index.js b/src/common/hooks/index.js index 82eb4ca1..9fcb4d7a 100644 --- a/src/common/hooks/index.js +++ b/src/common/hooks/index.js @@ -1,7 +1,9 @@ export * from './useAcqMethods'; +export * from './useBankingAccountTypes'; export * from './useBankingInformationSettings'; export * from './useIntegrationConfig'; export * from './useIntegrationConfigMutation'; export * from './useLinkedAgreements'; +export * from './useOrganizationBankingInformation'; export * from './useTranslatedCategories'; export * from './useTypes'; diff --git a/src/common/hooks/useBankingAccountTypes/index.js b/src/common/hooks/useBankingAccountTypes/index.js new file mode 100644 index 00000000..c1574cdd --- /dev/null +++ b/src/common/hooks/useBankingAccountTypes/index.js @@ -0,0 +1 @@ +export { useBankingAccountTypes } from './useBankingAccountTypes'; diff --git a/src/common/hooks/useBankingAccountTypes/useBankingAccountTypes.js b/src/common/hooks/useBankingAccountTypes/useBankingAccountTypes.js new file mode 100644 index 00000000..aec118d8 --- /dev/null +++ b/src/common/hooks/useBankingAccountTypes/useBankingAccountTypes.js @@ -0,0 +1,40 @@ +import { useQuery } from 'react-query'; + +import { + useNamespace, + useOkapiKy, +} from '@folio/stripes/core'; +import { LIMIT_MAX } from '@folio/stripes-acq-components'; + +import { BANKING_ACCOUNT_TYPES_API } from '../../constants'; + +const DEFAULT_DATA = []; + +export const useBankingAccountTypes = () => { + const ky = useOkapiKy(); + const [namespace] = useNamespace({ key: 'banking-account-types' }); + + const { + data, + isFetching, + isLoading, + refetch, + } = useQuery( + [namespace], + () => { + const searchParams = { + limit: LIMIT_MAX, + }; + + return ky.get(BANKING_ACCOUNT_TYPES_API, { searchParams }).json(); + }, + ); + + return ({ + bankingAccountTypes: data?.bankingAccountTypes || DEFAULT_DATA, + totalRecords: data?.totalRecords, + isFetching, + isLoading, + refetch, + }); +}; diff --git a/src/common/hooks/useOrganizationBankingInformation/index.js b/src/common/hooks/useOrganizationBankingInformation/index.js new file mode 100644 index 00000000..b97f1380 --- /dev/null +++ b/src/common/hooks/useOrganizationBankingInformation/index.js @@ -0,0 +1 @@ +export { useOrganizationBankingInformation } from './useOrganizationBankingInformation'; diff --git a/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js b/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js new file mode 100644 index 00000000..245f947e --- /dev/null +++ b/src/common/hooks/useOrganizationBankingInformation/useOrganizationBankingInformation.js @@ -0,0 +1,45 @@ +import { useQuery } from 'react-query'; + +import { + useNamespace, + useOkapiKy, +} from '@folio/stripes/core'; + +import { BANKING_INFORMATION_API } from '../../constants'; + +const DEFAULT_DATA = []; + +export const useOrganizationBankingInformation = (organizationId, options = {}) => { + const ky = useOkapiKy(); + const [namespace] = useNamespace({ key: 'organization-banking-information' }); + + const queryOptions = { + ...options, + enabled: options.enabled && Boolean(organizationId), + }; + + const { + data, + isFetching, + isLoading, + refetch, + } = useQuery( + [namespace], + () => { + const searchParams = { + query: `organizationId==${organizationId}`, + }; + + return ky.get(BANKING_INFORMATION_API, { searchParams }).json(); + }, + queryOptions, + ); + + return ({ + bankingInformation: data?.bankingInformation || DEFAULT_DATA, + totalRecords: data?.totalRecords, + isFetching, + isLoading, + refetch, + }); +}; diff --git a/translations/ui-organizations/en.json b/translations/ui-organizations/en.json index ec12c26e..8f0de46d 100644 --- a/translations/ui-organizations/en.json +++ b/translations/ui-organizations/en.json @@ -12,6 +12,13 @@ "comingSoon": "Coming in a future release.", + "data.bankingInformation.accountType": "Account type", + "data.bankingInformation.address": "Address", + "data.bankingInformation.bankAccountNumber": "Bank account number", + "data.bankingInformation.bankName": "Bank name", + "data.bankingInformation.isPrimary": "Use as primary banking information", + "data.bankingInformation.notes": "Notes", + "data.bankingInformation.transitNumber": "Transit number", "data.contactTypes.address": "Address", "data.contactTypes.addressLine1": "Address 1", "data.contactTypes.addressLine2": "Address 2", @@ -84,6 +91,7 @@ "vendor.confirmation.message": "Warning. All vendor information, vendor terms, EDI information, and accounts will be deleted from this record.", "summary": "Summary", + "bankingInformation": "Banking information", "contactInformation": "Contact information", "contactPeople": "Contact people", "agreements": "Agreements", @@ -120,6 +128,7 @@ "summary.add": "Add alternative names", "summary.pleaseAddAltNames": "Please add alternative names", + "button.bankingInformation.add": "Add banking information", "button.cancel": "Cancel", "button.saveAndClose": "Save & close",