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 13 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
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
import React from 'react';
import { FormattedMessage } from 'react-intl';

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

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

class BankingAccountTypeSettings extends React.Component {
alisher-epam marked this conversation as resolved.
Show resolved Hide resolved
constructor(props) {
super(props);
this.connectedControlledVocab = props.stripes.connect(ControlledVocab);
}

render() {
const { stripes } = this.props;

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

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

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 ConnectedComponent = this.connectedControlledVocab;

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"
/>
);
}
}

BankingAccountTypeSettings.propTypes = {
stripes: stripesShape.isRequired,
};

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

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 stripes={stripesMock} resources={[]} />);

describe('BankingAccountTypeSettings', () => {
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,98 @@
import { FormattedMessage } from 'react-intl';
import { Field, Form } from 'react-final-form';
import { useQueryClient } from 'react-query';

import {
Button,
Col,
Headline,
Loading,
Pane,
RadioButton,
RadioButtonGroup,
Row,
} from '@folio/stripes/components';
import { useShowCallout } from '@folio/stripes-acq-components';
import {
useOkapiKy,
useNamespace,
} from '@folio/stripes/core';

import { useBankingInformation } from '../hooks';
import { SETTINGS_API } from '../constants';
usavkov-epam marked this conversation as resolved.
Show resolved Hide resolved

const BankingInformationSettings = () => {
const { enabled, key, id: bankingInformationId, isLoading } = useBankingInformation();
const ky = useOkapiKy();
const queryClient = useQueryClient();
const [namespace] = useNamespace({ key: 'banking-information-settings' });
const sendCallout = useShowCallout();

const onSubmit = async ({ value }) => {
try {
await ky.put(`${SETTINGS_API}/${bankingInformationId}`, {
json: { value, key },
});
queryClient.invalidateQueries([namespace]);
alisher-epam marked this conversation as resolved.
Show resolved Hide resolved
sendCallout({
type: 'success',
alisher-epam marked this conversation as resolved.
Show resolved Hide resolved
message: <FormattedMessage id="ui-organizations.settings.accountTypes.save.success.message" />,
});
} catch (error) {
sendCallout({
type: 'success',
alisher-epam marked this conversation as resolved.
Show resolved Hide resolved
message: <FormattedMessage id="settings.accountTypes.save.error.generic.message" />,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

app prefix in the label ID

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

and please test if it works correctly

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

updated

});
}
};

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

return (
<Pane
defaultWidth="fill"
id="banking-information"
paneTitle={<FormattedMessage id="ui-organizations.settings.bankingInformation" />}
alisher-epam marked this conversation as resolved.
Show resolved Hide resolved
>
<Row>
<Col xs={12}>
<Headline margin="none">
<FormattedMessage id="ui-organizations.settings.bankingInformation" />
</Headline>
</Col>
<Col xs={12}>
<Form
alisher-epam marked this conversation as resolved.
Show resolved Hide resolved
onSubmit={onSubmit}
initialValues={{ value: enabled }}
render={({ handleSubmit }) => (
<form onSubmit={handleSubmit}>
<Field
name="value"
component={RadioButtonGroup}
>
<RadioButton
label={<FormattedMessage id="ui-organizations.settings.bankingInformation.enabled" />}
id="enabled"
value="true"
/>
<RadioButton
label={<FormattedMessage id="ui-organizations.settings.bankingInformation.disabled" />}
id="disabled"
value="false"
/>
</Field>
<Button type="submit" buttonStyle="primary">
<FormattedMessage id="ui-organizations.settings.accountTypes.save.button" />
</Button>
</form>
)}
/>
</Col>
</Row>
</Pane>
);
};

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,85 @@
import { render, screen, act } 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 BankingInformationSettings from './BankingInformationSettings';
import { useBankingInformation } from '../hooks';

jest.mock('react-query', () => ({
...jest.requireActual('react-query'),
useQueryClient: jest.fn(() => ({
invalidateQueries: 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,
})),
}));

const renderBankingInformationSettings = () => render(
<BankingInformationSettings />,
);

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

const paneTitle = screen.getAllByText('ui-organizations.settings.bankingInformation');

expect(paneTitle).toHaveLength(2);
});

it('should display radio buttons', async () => {
renderBankingInformationSettings();

expect(screen.getByText('ui-organizations.settings.bankingInformation.enabled')).toBeInTheDocument();
expect(screen.getByText('ui-organizations.settings.bankingInformation.disabled')).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,
});
const mockPutMethod = jest.fn(() => ({
json: () => Promise.resolve('ok'),
}));

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

renderBankingInformationSettings();

const enabledButton = screen.getByText('ui-organizations.settings.bankingInformation.enabled');
const saveButton = screen.getByText('ui-organizations.settings.accountTypes.save.button');

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

expect(mockPutMethod).toHaveBeenCalled();
});
});
1 change: 1 addition & 0 deletions src/Settings/BankingInformationSettings/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export { default as BankingInformationSettings } from './BankingInformationSettings';
44 changes: 36 additions & 8 deletions src/Settings/SettingsPage.js
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import React from 'react';
import React, { useMemo } from 'react';
import { FormattedMessage } from 'react-intl';

import { Settings } from '@folio/stripes/smart-components';
import { Loading } from '@folio/stripes/components';

import { useBankingInformation } from './hooks';
import { CategorySettings } from './CategorySettings';
import { TypeSettings } from './TypeSettings';
import { BankingInformationSettings } from './BankingInformationSettings';
import { BankingAccountTypeSettings } from './BankingAccountTypeSettings';

const pages = [
{
Expand All @@ -19,14 +23,38 @@ const pages = [
perm: 'settings.organizations.enabled',
route: 'type',
},
{
component: BankingInformationSettings,
label: <FormattedMessage id="ui-organizations.settings.bankingInformation" />,
perm: 'settings.organizations.enabled',
route: 'banking-information',
},
];

const SettingsPage = (props) => (
<Settings
{...props}
pages={pages}
paneTitle={<FormattedMessage id="ui-organizations.settings.vendorSettings" />}
/>
);
const bankingAccountTypesPage = {
component: BankingAccountTypeSettings,
label: <FormattedMessage id="ui-organizations.settings.bankingAccountTypes" />,
perm: 'settings.organizations.enabled',
route: 'banking-account-types',
};

const SettingsPage = (props) => {
const { enabled, isLoading } = useBankingInformation();

const settingsPages = useMemo(() => (enabled ? pages.concat(bankingAccountTypesPage) : pages), [enabled]);

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

return (
<Settings
{...props}
key={settingsPages.length}
pages={settingsPages}
paneTitle={<FormattedMessage id="ui-organizations.settings.vendorSettings" />}
/>
);
};

export default SettingsPage;
Loading