Skip to content

Commit

Permalink
UIORGS-391 - Settings for banking information (#577)
Browse files Browse the repository at this point in the history
* UIORGS-391: add initial routing config and components

* UIORGS-404 bump optional plugins to compatible versions (#572)

Use plugins compatible with the requested version of stripes (v9).

Refs UIORGS-404

* Release v5.0.0 (#573)

* UIORGS-383: add donor info for organization summary (#574)

* UIORGS-383: add donor info for organization summary

* tests: fix failing tests

* tests: remove failing snapshots

* tests: generate snapshot tests

* tests: update snapshot tests

* improve: bump package.json, fix organizations summary layout, remove checkbox handler

* feat: add CRUD for banking account types

* UIORGS-391: integrate with backend.

* tests: add test coverages

* remove failing test

* tests: add test cases

* tests: add test coverages

* tests: add test coverage

* tests: add test coverages

* refactor: code based on comments

* test: fix failing tests

* update package.json interfaces

* update: refactor code and component

* test: add test coverage

* update: permissions for organization settings

* update: permissions for organizations settings

* refactor: query key and checkbox component

* remove unnecessary permissions

* update: permissions for banking-account-types

* refactor: change the order of imports

* refactor: fix column naming issues

* optimize: add settings version for optimistic locking issue

* tests: fix failing tests

---------

Co-authored-by: Zak Burke <[email protected]>
  • Loading branch information
alisher-epam and zburke authored Nov 1, 2023
1 parent 06817ed commit 797cca8
Show file tree
Hide file tree
Showing 17 changed files with 620 additions and 10 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
## 5.1.0 (IN PROGRESS)

* Designate Organization as donor. Refs UIORGS-383.
* Settings for banking information. Refs UIORGS-391.

## [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
13 changes: 11 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
"organizations-storage.emails": "1.1",
"organizations-storage.interfaces": "2.1",
"organizations-storage.phone-numbers": "2.0",
"organizations-storage.settings": "1.0",
"organizations-storage.urls": "1.1",
"tags": "1.0",
"users": "15.1 16.0"
Expand Down Expand Up @@ -207,7 +208,11 @@
"settings.organizations.enabled",
"organizations-storage.organization-types.collection.get",
"organizations-storage.organization-types.item.get",
"organizations-storage.categories.collection.get"
"organizations-storage.categories.collection.get",
"organizations-storage.settings.collection.get",
"organizations-storage.settings.item.get",
"organizations-storage.banking-account-types.collection.get",
"organizations-storage.banking-account-types.item.get"
]
},
{
Expand All @@ -221,7 +226,11 @@
"organizations-storage.organization-types.item.delete",
"organizations-storage.categories.item.delete",
"organizations-storage.categories.item.post",
"organizations-storage.categories.item.put"
"organizations-storage.categories.item.put",
"organizations-storage.settings.item.put",
"organizations-storage.banking-account-types.item.post",
"organizations-storage.banking-account-types.item.put",
"organizations-storage.banking-account-types.item.delete"
]
}
]
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';

import { getControlledVocabTranslations } from '@folio/stripes-acq-components';
import { useStripes } from '@folio/stripes/core';
import { ControlledVocab } from '@folio/stripes/smart-components';

import { BANKING_ACCOUNT_TYPES_API } from '../constants';

const setUniqValidation = (value, index, items) => {
const errors = {};

const isBankingAccountTypeExist = items.some(({ id, name }) => {
return name?.toLowerCase() === value?.name?.toLowerCase() && id !== value?.id;
});

if (isBankingAccountTypeExist) {
errors.name = <FormattedMessage id="ui-organizations.settings.accountTypes.save.error.accountTypeMustBeUnique" />;
}

return errors;
};

const BankingAccountTypeSettings = () => {
const stripes = useStripes();
const ConnectedComponent = stripes.connect(ControlledVocab);

const columnMapping = {
name: <FormattedMessage id="ui-organizations.settings.accountTypes.name" />,
action: <FormattedMessage id="ui-organizations.settings.accountTypes.action" />,
};

const hasEditPerms = stripes.hasPerm('ui-organizations.settings');
const actionSuppressor = {
edit: () => !hasEditPerms,
delete: () => !hasEditPerms,
};

return (
<ConnectedComponent
actionSuppressor={actionSuppressor}
canCreate={hasEditPerms}
stripes={stripes}
baseUrl={BANKING_ACCOUNT_TYPES_API}
records="bankingAccountTypes"
label={<FormattedMessage id="ui-organizations.settings.bankingAccountTypes" />}
translations={getControlledVocabTranslations('ui-organizations.settings.bankingAccountTypes')}
objectLabel="BankingAccountTypes"
visibleFields={['name']}
columnMapping={columnMapping}
hiddenFields={['lastUpdated', 'numberOfObjects']}
nameKey="bankingAccountTypes"
id="bankingAccountTypes"
validate={setUniqValidation}
sortby="name"
/>
);
};

export default BankingAccountTypeSettings;
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import {
render,
screen,
} from '@folio/jest-config-stripes/testing-library/react';
import { useStripes } from '@folio/stripes/core';
import { ControlledVocab } from '@folio/stripes/smart-components';

import BankingAccountTypeSettings from './BankingAccountTypeSettings';

jest.mock('@folio/stripes/core', () => ({
...jest.requireActual('@folio/stripes/core'),
useStripes: jest.fn(),
}));

jest.mock('@folio/stripes-smart-components/lib/ControlledVocab', () => jest.fn(({
rowFilter,
label,
rowFilterFunction,
preCreateHook,
listSuppressor,
}) => (
<>
{label}
<div onChange={rowFilterFunction}>{rowFilter}</div>
<button
data-testid="button-new"
type="button"
onClick={() => {
preCreateHook();
listSuppressor();
}}
>
New
</button>
</>
)));

const stripesMock = {
connect: component => component,
hasPerm: jest.fn(() => true),
clone: jest.fn(),
};

const renderCategorySettings = () => render(<BankingAccountTypeSettings />);

describe('BankingAccountTypeSettings', () => {
beforeEach(() => {
useStripes.mockReturnValue(stripesMock);
});

it('should render component', () => {
renderCategorySettings();

expect(screen.getByText('New'));
expect(screen.getByText('ui-organizations.settings.bankingAccountTypes'));
});

it('should check action suppression', () => {
renderCategorySettings();

const { actionSuppressor } = ControlledVocab.mock.calls[0][0];

expect(actionSuppressor.edit()).toBeFalsy();
expect(actionSuppressor.delete()).toBeFalsy();
});
});
1 change: 1 addition & 0 deletions src/Settings/BankingAccountTypeSettings/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as BankingAccountTypeSettings } from './BankingAccountTypeSettings';
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import { FormattedMessage } from 'react-intl';

import { useShowCallout } from '@folio/stripes-acq-components';
import { Loading } from '@folio/stripes/components';
import { useOkapiKy } from '@folio/stripes/core';

import { SETTINGS_API } from '../constants';
import { useBankingInformation } from '../hooks';
import BankingInformationSettingsForm from './BankingInformationSettingsForm';

const BankingInformationSettings = () => {
const {
enabled,
key,
id: bankingInformationId,
version,
isLoading,
refetch,
} = useBankingInformation();
const ky = useOkapiKy();
const sendCallout = useShowCallout();

const onSubmit = async ({ value }) => {
try {
await ky.put(`${SETTINGS_API}/${bankingInformationId}`, {
json: { value, key, _version: version },
});

refetch();
sendCallout({
message: <FormattedMessage id="ui-organizations.settings.accountTypes.save.success.message" />,
});
} catch (error) {
sendCallout({
type: 'error',
message: <FormattedMessage id="ui-organizations.settings.accountTypes.save.error.generic.message" />,
});
}
};

if (isLoading) {
return <Loading />;
}

return (
<BankingInformationSettingsForm
onSubmit={onSubmit}
initialValues={{ value: enabled }}
/>
);
};

export default BankingInformationSettings;
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
import { MemoryRouter } from 'react-router-dom';

import {
act,
render,
screen,
} from '@folio/jest-config-stripes/testing-library/react';
import user from '@folio/jest-config-stripes/testing-library/user-event';
import { useOkapiKy } from '@folio/stripes/core';

import { useBankingInformation } from '../hooks';
import BankingInformationSettings from './BankingInformationSettings';

const mockRefetch = jest.fn();

jest.mock('@folio/stripes/components', () => ({
...jest.requireActual('@folio/stripes/components'),
Loading: () => <div>Loading</div>,
}));

jest.mock('../hooks', () => ({
useBankingInformation: jest.fn(() => ({
isLoading: false,
enabled: false,
refetch: mockRefetch,
})),
}));

const renderBankingInformationSettings = () => render(
<BankingInformationSettings />,
{ wrapper: MemoryRouter },
);

describe('BankingInformationSettings component', () => {
it('should display pane headings', () => {
renderBankingInformationSettings();

const paneTitle = screen.getByText('ui-organizations.settings.bankingInformation');
const checkboxLabel = screen.getByText('ui-organizations.settings.bankingInformation.enable');

expect(paneTitle).toBeInTheDocument();
expect(checkboxLabel).toBeInTheDocument();
});

it('should render Loading component', () => {
useBankingInformation.mockReturnValue({
isLoading: true,
enabled: false,
});

renderBankingInformationSettings();

expect(screen.getByText('Loading')).toBeInTheDocument();
});

it('should save banking options', async () => {
useBankingInformation.mockClear().mockReturnValue({
isLoading: false,
enabled: true,
refetch: mockRefetch,
});
const mockPutMethod = jest.fn(() => ({
json: () => Promise.resolve('ok'),
}));

useOkapiKy
.mockClear()
.mockReturnValue({
put: mockPutMethod,
});

renderBankingInformationSettings();

const checkbox = screen.getByRole('checkbox', { name: 'ui-organizations.settings.bankingInformation.enable' });
const saveButton = screen.getByText('ui-organizations.settings.accountTypes.save.button');

await act(async () => {
await user.click(checkbox);
await user.click(saveButton);
});

expect(mockPutMethod).toHaveBeenCalled();
});
});
Original file line number Diff line number Diff line change
@@ -0,0 +1,73 @@
import { useMemo } from 'react';
import PropTypes from 'prop-types';
import { Field } from 'react-final-form';
import { FormattedMessage } from 'react-intl';

import {
Button,
Checkbox,
Col,
Pane,
PaneFooter,
PaneHeader,
Row,
} from '@folio/stripes/components';
import stripesForm from '@folio/stripes/final-form';

const BankingInformationSettingsForm = ({
handleSubmit,
pristine,
submitting,
}) => {
const paneFooter = useMemo(() => {
const end = (
<Button
id="clickable-save-contact-person-footer"
type="submit"
buttonStyle="primary mega"
disabled={pristine || submitting}
onClick={handleSubmit}
>
<FormattedMessage id="ui-organizations.settings.accountTypes.save.button" />
</Button>
);

return <PaneFooter renderEnd={end} />;
}, [handleSubmit, pristine, submitting]);

const paneTitle = <FormattedMessage id="ui-organizations.settings.bankingInformation" />;

return (
<Pane
defaultWidth="fill"
id="banking-information"
renderHeader={renderProps => <PaneHeader {...renderProps} paneTitle={paneTitle} />}
footer={paneFooter}
>
<Row>
<Col xs={12}>
<Field
component={Checkbox}
label={<FormattedMessage id="ui-organizations.settings.bankingInformation.enable" />}
name="value"
type="checkbox"
/>
</Col>
</Row>
</Pane>
);
};

BankingInformationSettingsForm.propTypes = {
handleSubmit: PropTypes.func.isRequired,
pristine: PropTypes.bool.isRequired,
submitting: PropTypes.bool.isRequired,
renderHeader: PropTypes.func,
};

export default stripesForm({
enableReinitialize: true,
keepDirtyOnReinitialize: true,
navigationCheck: true,
subscription: { values: true },
})(BankingInformationSettingsForm);
Loading

0 comments on commit 797cca8

Please sign in to comment.