Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UIORGS-391 - Settings for banking information #577

Merged
merged 28 commits into from
Nov 1, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
2b8e839
UIORGS-391: add initial routing config and components
alisher-epam Oct 11, 2023
04b922a
UIORGS-404 bump optional plugins to compatible versions (#572)
zburke Oct 11, 2023
0c5efe6
Release v5.0.0 (#573)
alisher-epam Oct 12, 2023
cdcf2cf
UIORGS-383: add donor info for organization summary (#574)
alisher-epam Oct 16, 2023
4ca7e50
feat: add CRUD for banking account types
alisher-epam Oct 18, 2023
c05b8e2
UIORGS-391: integrate with backend.
alisher-epam Oct 20, 2023
5884302
Merge branch 'master' into UIORGS-391
alisher-epam Oct 20, 2023
d46a134
tests: add test coverages
alisher-epam Oct 20, 2023
7b3e655
remove failing test
alisher-epam Oct 20, 2023
8efd321
tests: add test cases
alisher-epam Oct 20, 2023
dfa458b
tests: add test coverages
alisher-epam Oct 23, 2023
2210b9c
tests: add test coverage
alisher-epam Oct 23, 2023
b8aa6d8
tests: add test coverages
alisher-epam Oct 23, 2023
ab5e6cc
refactor: code based on comments
alisher-epam Oct 25, 2023
ab7c046
test: fix failing tests
alisher-epam Oct 25, 2023
6906e9f
update package.json interfaces
alisher-epam Oct 25, 2023
e2ae13b
update: refactor code and component
alisher-epam Oct 27, 2023
bc9d7b0
test: add test coverage
alisher-epam Oct 27, 2023
d457b42
update: permissions for organization settings
alisher-epam Oct 27, 2023
fcd0434
update: permissions for organizations settings
alisher-epam Oct 27, 2023
81f4927
refactor: query key and checkbox component
alisher-epam Oct 27, 2023
6a5624c
remove unnecessary permissions
alisher-epam Oct 27, 2023
43871bc
update: permissions for banking-account-types
alisher-epam Oct 28, 2023
cba5045
refactor: change the order of imports
alisher-epam Oct 31, 2023
47bf0c9
Merge branch 'master' into UIORGS-391
alisher-epam Oct 31, 2023
3eed623
refactor: fix column naming issues
alisher-epam Nov 1, 2023
e2f626a
optimize: add settings version for optimistic locking issue
alisher-epam Nov 1, 2023
8e0010c
tests: fix failing tests
alisher-epam Nov 1, 2023
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
alisher-epam marked this conversation as resolved.
Show resolved Hide resolved
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);
usavkov-epam marked this conversation as resolved.
Show resolved Hide resolved

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']}
usavkov-epam marked this conversation as resolved.
Show resolved Hide resolved
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';
usavkov-epam marked this conversation as resolved.
Show resolved Hide resolved
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;
usavkov-epam marked this conversation as resolved.
Show resolved Hide resolved
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
Loading