Skip to content

Commit

Permalink
UIORGS-390 Implement organization's banking information form (#580)
Browse files Browse the repository at this point in the history
* UIORGS-390 Move reusable constants and hooks to appropriate places

* UIORGS-390 rename hook useBankingInformation to useBankingInformationSettings: to not confuse domens

* UIORGS-390 Implement base components and hooks for banking information (no logic)

* UIORGS-390 Implement logic for banking information management

* UIORGS-390 add hook to get categories

* UIORGS-390 Add categories options to the form; fix issues

* UIORGS-390 Update label for address category

* UIORGS-390 use translated categories

* UIORGS-390 Implement event emmiter to handle address categories change

* UIORGS-390 Update changelog

* Fix countries sorting

* update tests

* fix code smells

* UIORGS-390 Add unit tests

* add more test

* UIORGS-390 add tests to cover categories assignement logic

* UIORGS-390 Temporarily include banking info perms into common sets
  • Loading branch information
usavkov-epam authored Nov 16, 2023
1 parent 485f859 commit e780e54
Show file tree
Hide file tree
Showing 68 changed files with 2,635 additions and 1,139 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@

* Designate Organization as donor. Refs UIORGS-383.
* Settings for banking information. Refs UIORGS-391.
* Implement organization's banking information form. Refs UIORGS-390.

## [5.0.0](https://github.com/folio-org/ui-organizations/tree/v5.0.0) (2023-10-12)
[Full Changelog](https://github.com/folio-org/ui-organizations/compare/v4.0.0...v5.0.0)
Expand Down
6 changes: 6 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down Expand Up @@ -80,6 +81,8 @@
"data-export.config.item.get",
"orders.acquisition-methods.collection.get",
"orders.acquisition-method.item.get",
"organizations.banking-information.collection.get",
"organizations.banking-information.item.get",
"organizations.organizations.collection.get",
"organizations.organizations.item.get",
"organizations-storage.addresses.collection.get",
Expand Down Expand Up @@ -108,6 +111,7 @@
"data-export.config.item.delete",
"data-export.config.item.post",
"data-export.config.item.put",
"organizations.banking-information.item.put",
"organizations.organizations.item.put",
"organizations-storage.addresses.item.put",
"organizations-storage.emails.item.put",
Expand All @@ -127,6 +131,7 @@
"displayName": "Organizations: View, edit, create",
"visible": true,
"subPermissions": [
"organizations.banking-information.item.post",
"organizations.organizations.item.post",
"organizations-storage.addresses.item.post",
"organizations-storage.emails.item.post",
Expand All @@ -140,6 +145,7 @@
"displayName": "Organizations: View, edit, delete",
"visible": true,
"subPermissions": [
"organizations.banking-information.item.delete",
"organizations.organizations.item.delete",
"organizations-storage.addresses.item.delete",
"organizations-storage.emails.item.delete",
Expand Down
39 changes: 28 additions & 11 deletions src/Organizations/OrganizationCreate/OrganizationCreate.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
import React, { useCallback } from 'react';
import PropTypes from 'prop-types';
import { withRouter } from 'react-router-dom';
import { useCallback, useMemo } from 'react';
import { useIntl } from 'react-intl';
import { withRouter } from 'react-router-dom';

import {
stripesConnect,
} from '@folio/stripes/core';
import { stripesConnect } from '@folio/stripes/core';
import { useShowCallout } from '@folio/stripes-acq-components';

import { VIEW_ORG_DETAILS } from '../../common/constants';
import { organizationsResource } from '../../common/resources';
import {
OrganizationForm,
} from '../OrganizationForm';
import { BANKING_INFORMATION_FIELD_NAME } from '../constants';
import { handleSaveErrorResponse } from '../handleSaveErrorResponse';
import { OrganizationForm } from '../OrganizationForm';
import { useBankingInformationManager } from '../useBankingInformationManager';

const INITIAL_VALUES = {
interfaces: [],
Expand All @@ -32,6 +30,8 @@ const INITIAL_VALUES = {
};

export const OrganizationCreate = ({ history, location, mutator }) => {
const { manageBankingInformation } = useBankingInformationManager();

const cancelForm = useCallback(
(id) => {
history.push({
Expand All @@ -45,9 +45,21 @@ export const OrganizationCreate = ({ history, location, mutator }) => {

const showCallout = useShowCallout();
const intl = useIntl();

const createOrganization = useCallback(
(data) => {
(values, { getFieldState }) => {
const { [BANKING_INFORMATION_FIELD_NAME]: bankingInformation, ...data } = values;

return mutator.createOrganizationOrg.POST(data)
.then(async organization => {
await manageBankingInformation({
initBankingInformation: getFieldState(BANKING_INFORMATION_FIELD_NAME)?.initial,
bankingInformation,
organization,
});

return organization;
})
.then(organization => {
setTimeout(() => cancelForm(organization.id));
showCallout({
Expand All @@ -60,12 +72,17 @@ export const OrganizationCreate = ({ history, location, mutator }) => {
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[cancelForm, intl, showCallout],
[cancelForm, intl, manageBankingInformation, showCallout],
);

const initialValues = useMemo(() => ({
[BANKING_INFORMATION_FIELD_NAME]: [],
...INITIAL_VALUES,
}), []);

return (
<OrganizationForm
initialValues={INITIAL_VALUES}
initialValues={initialValues}
onSubmit={createOrganization}
cancelForm={cancelForm}
/>
Expand Down
42 changes: 38 additions & 4 deletions src/Organizations/OrganizationCreate/OrganizationCreate.test.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
import React from 'react';
import { QueryClient, QueryClientProvider } from 'react-query';
import { render, screen } from '@folio/jest-config-stripes/testing-library/react';

import { OrganizationForm } from '../OrganizationForm';

import { useBankingInformationManager } from '../useBankingInformationManager';
import { OrganizationCreate } from './OrganizationCreate';

jest.mock('../OrganizationForm', () => ({
OrganizationForm: jest.fn().mockReturnValue('OrganizationForm'),
}));
jest.mock('../useBankingInformationManager', () => ({
useBankingInformationManager: jest.fn(),
}));

const mutatorMock = {
createOrganizationOrg: {
Expand All @@ -17,21 +20,40 @@ const mutatorMock = {
const historyMock = {
push: jest.fn(),
};

const getFieldState = jest.fn();
const manageBankingInformation = jest.fn();

const queryClient = new QueryClient();

const wrapper = ({ children }) => (
<QueryClientProvider client={queryClient}>
{children}
</QueryClientProvider>
);

const renderOrganizationCreate = (props) => render(
<OrganizationCreate
location={{}}
history={historyMock}
mutator={mutatorMock}
{...props}
/>,
{ wrapper },
);

describe('OrganizationCreate', () => {
beforeEach(() => {
OrganizationForm.mockClear();

getFieldState.mockClear();
historyMock.push.mockClear();
manageBankingInformation.mockClear();
mutatorMock.createOrganizationOrg.POST.mockClear();

useBankingInformationManager
.mockClear()
.mockReturnValue({ manageBankingInformation });
});

it('should display organization form', () => {
Expand All @@ -56,11 +78,23 @@ describe('OrganizationCreate', () => {
expect(historyMock.push.mock.calls[0][0].pathname).toBe('/organizations/view/orgUid');
});

it('should save organization', () => {
it('should save organization', async () => {
mutatorMock.createOrganizationOrg.POST.mockReturnValue(Promise.resolve({ id: 'orgUid' }));

renderOrganizationCreate();

OrganizationForm.mock.calls[0][0].onSubmit({});
await OrganizationForm.mock.calls[0][0].onSubmit({}, { getFieldState });

expect(mutatorMock.createOrganizationOrg.POST).toHaveBeenCalled();
});

it('should handle banking information on form submit', async () => {
mutatorMock.createOrganizationOrg.POST.mockReturnValue(Promise.resolve({ id: 'orgUid' }));

renderOrganizationCreate();

await OrganizationForm.mock.calls[0][0].onSubmit({}, { getFieldState });

expect(manageBankingInformation).toHaveBeenCalled();
});
});
43 changes: 34 additions & 9 deletions src/Organizations/OrganizationEdit/OrganizationEdit.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React, {
import {
useCallback,
useEffect,
useMemo,
useState,
} from 'react';
import { FormattedMessage, useIntl } from 'react-intl';
Expand All @@ -19,27 +20,35 @@ 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,
} from '../OrganizationForm';
import { BANKING_INFORMATION_FIELD_NAME } from '../constants';
import { OrganizationForm } from '../OrganizationForm';
import { handleSaveErrorResponse } from '../handleSaveErrorResponse';
import { useBankingInformationManager } from '../useBankingInformationManager';

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 { manageBankingInformation } = useBankingInformationManager();

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
[],
Expand All @@ -58,8 +67,17 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => {
);

const updateOrganization = useCallback(
(data) => {
(values, { getFieldState }) => {
const { [BANKING_INFORMATION_FIELD_NAME]: bankingInformation, ...data } = values;

return mutator.editOrganizationOrg.PUT(data)
.then(() => {
return manageBankingInformation({
initBankingInformation: getFieldState(BANKING_INFORMATION_FIELD_NAME)?.initial,
bankingInformation,
organization: values,
});
})
.then(() => {
setTimeout(cancelForm);
showCallout({
Expand All @@ -72,9 +90,16 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => {
});
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[cancelForm, intl, showCallout],
[cancelForm, intl, manageBankingInformation, showCallout],
);

const initialValues = useMemo(() => ({
[BANKING_INFORMATION_FIELD_NAME]: bankingInformationData,
...organization,
}), [organization, bankingInformationData]);

const isLoading = isOrganizationLoading || isBankingInformationLoading;

if (isLoading) {
return (
<Paneset>
Expand All @@ -85,7 +110,7 @@ export const OrganizationEdit = ({ match, history, location, mutator }) => {

return (
<OrganizationForm
initialValues={organization}
initialValues={initialValues}
onSubmit={updateOrganization}
cancelForm={cancelForm}
paneTitle={<FormattedMessage id="ui-organizations.editOrg.title" values={{ name: organization.name }} />}
Expand Down
Loading

0 comments on commit e780e54

Please sign in to comment.