Skip to content

Commit

Permalink
UIORGS-336 Create or change vendor code using number generator (#536)
Browse files Browse the repository at this point in the history
Refs UXPROD-3891, UIORGS-336, UIORGS-337
  • Loading branch information
EthanFreestone authored Dec 11, 2024
1 parent e94bc6f commit e02c827
Show file tree
Hide file tree
Showing 19 changed files with 647 additions and 17 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
* Adapt organization metadata fields to version history mechanism. Refs UIORGS-359.
* Add claiming to organization integration details. Refs UIORGS-442.
* Add "Duplicate" integration action to organization integration view. Refs UIORGS-441.
* *BREAKING* Add number generator for vendor code including settings page. Refs UIORGS-336, UIORGS-337.

## [5.2.0](https://github.com/folio-org/ui-organizations/tree/v5.2.0) (2024-10-31)
[Full Changelog](https://github.com/folio-org/ui-organizations/compare/v5.1.1...v5.2.0)
Expand Down
24 changes: 22 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,11 +32,14 @@
"organizations-storage.interfaces": "2.1",
"organizations-storage.phone-numbers": "2.0",
"organizations-storage.privileged-contacts": "1.0",
"organizations-storage.settings": "1.0",
"organizations-storage.settings": "1.1",
"organizations-storage.urls": "1.1",
"tags": "1.0",
"users": "15.1 16.0"
},
"optionalOkapiInterfaces": {
"servint": "4.0"
},
"queryResource": "query",
"icons": [
{
Expand All @@ -46,6 +49,7 @@
}
],
"stripesDeps": [
"@folio/service-interaction",
"@folio/stripes-acq-components"
],
"permissionSets": [
Expand All @@ -64,7 +68,9 @@
{
"permissionName": "ui-organizations.third-party-services.execute",
"displayName": "Organizations: Permissions required to call services apart from mod-organizations-storage",
"replaces": ["ui-organizations.third-party-services"],
"replaces": [
"ui-organizations.third-party-services"
],
"visible": false,
"subPermissions": [
"acquisition.organization.events.get",
Expand Down Expand Up @@ -297,6 +303,17 @@
"organizations-storage.banking-account-types.item.put",
"organizations-storage.banking-account-types.item.delete"
]
},
{
"permissionName": "ui-organizations.settings.numberGenerator.manage",
"displayName": "Settings (Organizations): Manage number generator options",
"subPermissions": [
"settings.organizations.enabled",
"organizations-storage.settings.collection.get",
"organizations-storage.settings.item.post",
"organizations-storage.settings.item.put"
],
"visible": true
}
]
},
Expand Down Expand Up @@ -327,8 +344,10 @@
"@bigtest/react": "^0.1.2",
"@folio/eslint-config-stripes": "^7.0.0",
"@folio/jest-config-stripes": "^2.0.0",
"@folio/service-interaction": "^3.1.0",
"@folio/stripes": "^9.0.0",
"@folio/stripes-cli": "^3.0.0",
"@folio/stripes-erm-components": "^9.2.0",
"@formatjs/cli": "^6.1.3",
"babel-jest": "^26.3.0",
"chai": "^4.2.0",
Expand Down Expand Up @@ -367,6 +386,7 @@
"redux-form": "^8.3.0"
},
"peerDependencies": {
"@folio/service-interaction": "^3.1.0",
"@folio/stripes": "^9.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
Expand Down
Original file line number Diff line number Diff line change
@@ -1,30 +1,67 @@
import React, { useCallback } from 'react';
import { Field } from 'react-final-form';
import {
Field,
useForm,
} from 'react-final-form';
import { FormattedMessage } from 'react-intl';
import PropTypes from 'prop-types';

import { NumberGeneratorModalButton } from '@folio/service-interaction';
import { stripesConnect } from '@folio/stripes/core';
import { TextField } from '@folio/stripes/components';
import {
Col,
Row,
TextField,
} from '@folio/stripes/components';

import { VENDOR_CODE_GENERATOR_CODE } from '../../../../common/constants';
import { useVendorCodeGeneratorSettings } from '../../../../common/hooks';
import { fetchOrgsByParam } from '../../../../common/resources';
import { validateOrgCode } from './validateOrgCode';

const FieldCode = ({ orgId, mutator }) => {
const { change, resetFieldState } = useForm();
const { isUseGenerator, isUseBoth } = useVendorCodeGeneratorSettings();

const validate = useCallback(value => {
return validateOrgCode(mutator.fetchOrgByCode, orgId, value);
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[orgId]);

const handleGeneratedValue = useCallback((generatedValue) => {
change('code', generatedValue);
resetFieldState('code');
}, [change, resetFieldState]);

return (
<Field
component={TextField}
fullWidth
label={<FormattedMessage id="ui-organizations.summary.code" />}
name="code"
required
validate={validate}
/>
<Row>
<Col xs={12}>
<Field
component={TextField}
disabled={isUseGenerator}
fullWidth
label={<FormattedMessage id="ui-organizations.summary.code" />}
name="code"
required
validate={validate}
/>
</Col>
{(isUseGenerator || isUseBoth) && (
<Col xs={12}>
<NumberGeneratorModalButton
buttonLabel={<FormattedMessage id="ui-organizations.numberGenerator.generateVendorCode" />}
callback={handleGeneratedValue}
generateButtonLabel={<FormattedMessage id="ui-organizations.numberGenerator.generateVendorCode" />}
generator={VENDOR_CODE_GENERATOR_CODE}
id="vendor-code-generator"
modalProps={{
label: <FormattedMessage id="ui-organizations.numberGenerator.vendorCodeGenerator" />,
}}
/>
</Col>
)}
</Row>
);
};

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import React from 'react';
import userEvent from '@folio/jest-config-stripes/testing-library/user-event';
import { render, screen } from '@folio/jest-config-stripes/testing-library/react';
import { MemoryRouter } from 'react-router-dom';

Expand All @@ -8,10 +9,25 @@ import { organization, organizationTypes } from 'fixtures';
import { QueryClient, QueryClientProvider } from 'react-query';
import OrganizationSummaryForm from './OrganizationSummaryForm';

import { useTypes } from '../../../common/hooks';
import {
useTypes,
useVendorCodeGeneratorSettings,
} from '../../../common/hooks';

jest.mock('../../../common/hooks', () => ({
useTypes: jest.fn(),
useVendorCodeGeneratorSettings: jest.fn(),
}));

jest.mock('@folio/service-interaction', () => ({
NumberGeneratorModalButton: ({ callback }) => (
<button
onClick={() => callback('abc123')}
type="button"
>
NumberGeneratorModalButton
</button>
),
}));

const TestForm = stripesFinalForm({})(
Expand Down Expand Up @@ -44,6 +60,11 @@ describe('OrganizationSummaryForm', () => {
useTypes
.mockClear()
.mockReturnValue({ organizationTypes, totalRecords: organizationTypes.length });
useVendorCodeGeneratorSettings.mockReturnValue({
isUseGenerator: false,
isUseBoth: false,
isUseTextfield: false,
});
});

afterEach(() => {
Expand Down Expand Up @@ -73,4 +94,41 @@ describe('OrganizationSummaryForm', () => {

expect((selectedType).length).toEqual(1);
});

it('should not render the NumberGeneratorModalButton', async () => {
renderForm();

expect(screen.getByRole('textbox', { name: 'ui-organizations.summary.code' })).toBeEnabled();
expect(screen.queryByRole('button', { name: 'NumberGeneratorModalButton' })).not.toBeInTheDocument();
});

it('should render the NumberGeneratorModalButton when setting=isUseGenerator', async () => {
useVendorCodeGeneratorSettings.mockReturnValue({ isUseGenerator: true });

renderForm();

expect(screen.getByRole('textbox', { name: 'ui-organizations.summary.code' })).toBeDisabled();
expect(screen.getByRole('button', { name: 'NumberGeneratorModalButton' })).toBeInTheDocument();
});

it('should render the NumberGeneratorModalButton when setting=isUseBoth', async () => {
useVendorCodeGeneratorSettings.mockReturnValue({ isUseBoth: true });

renderForm();

expect(screen.getByRole('textbox', { name: 'ui-organizations.summary.code' })).toBeEnabled();
expect(screen.getByRole('button', { name: 'NumberGeneratorModalButton' })).toBeInTheDocument();
});

it('should update vendor code field value when NumberGeneratorModalButton is clicked', async () => {
useVendorCodeGeneratorSettings.mockReturnValue({ isUseBoth: true });

renderForm();
const button = screen.getByRole('button', { name: 'NumberGeneratorModalButton' });
const input = screen.getByRole('textbox', { name: 'ui-organizations.summary.code' });

expect(input).toHaveValue('');
await userEvent.click(button);
expect(input).toHaveValue('abc123');
});
});
52 changes: 52 additions & 0 deletions src/Settings/NumberGeneratorSettings/NumberGeneratorSettings.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
import { FormattedMessage } from 'react-intl';

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

import NumberGeneratorSettingsForm from './NumberGeneratorSettingsForm';
import { SETTINGS_API } from '../../common/constants/api';
import { VENDOR_CODE_GENERATOR_SETTINGS_KEY } from '../../common/constants/numberGenerator';
import { useVendorCodeGeneratorSettings } from '../../common/hooks/useVendorCodeGeneratorSettings';

const NumberGeneratorSettings = () => {
const { vendorCodeSetting, isLoading } = useVendorCodeGeneratorSettings();
const ky = useOkapiKy();
const sendCallout = useShowCallout();

const onSubmit = async ({ [VENDOR_CODE_GENERATOR_SETTINGS_KEY]: value }) => {
try {
if (vendorCodeSetting) {
await ky.put(`${SETTINGS_API}/${vendorCodeSetting.id}`, {
json: { ...vendorCodeSetting, value },
});
} else {
await ky.post(SETTINGS_API, {
json: { key: VENDOR_CODE_GENERATOR_SETTINGS_KEY, value },
});
}

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

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

return (
<NumberGeneratorSettingsForm
initialValues={{ [VENDOR_CODE_GENERATOR_SETTINGS_KEY]: vendorCodeSetting?.value }}
onSubmit={onSubmit}
/>
);
};

export default NumberGeneratorSettings;
Loading

0 comments on commit e02c827

Please sign in to comment.