diff --git a/CHANGELOG.md b/CHANGELOG.md index 95e13c83..12d1130a 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -40,6 +40,7 @@ * Implement feature toggle for ECS and not ECS envs. Refs UIREQ-1171. * Hide proxy functionality for ECS with mod-tlr enabled. Refs UIREQ-1185. * Update permission after mod-patron-blocks permission changes. Refs UIREQ-1201. +* Remove feature toggle functionality. Refs UIREQ-1199. ## [10.0.2] (https://github.com/folio-org/ui-requests/tree/v10.0.2) (2024-11-22) [Full Changelog](https://github.com/folio-org/ui-requests/compare/v10.0.1...v10.0.2) diff --git a/src/deprecated/components/InstanceInformation/InstanceInformation.css b/src/deprecated/components/InstanceInformation/InstanceInformation.css deleted file mode 100644 index 97c034ab..00000000 --- a/src/deprecated/components/InstanceInformation/InstanceInformation.css +++ /dev/null @@ -1,3 +0,0 @@ -.enterButton { - margin-top: 25px; -} diff --git a/src/deprecated/components/InstanceInformation/InstanceInformation.js b/src/deprecated/components/InstanceInformation/InstanceInformation.js deleted file mode 100644 index c6eafab7..00000000 --- a/src/deprecated/components/InstanceInformation/InstanceInformation.js +++ /dev/null @@ -1,265 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; -import { Field } from 'react-final-form'; -import { isNull } from 'lodash'; - -import { - Button, - Col, - Icon, - Row, - TextField, -} from '@folio/stripes/components'; -import { Pluggable } from '@folio/stripes/core'; - -import { - BASE_SPINNER_PROPS, - ENTER_EVENT_KEY, - REQUEST_FORM_FIELD_NAMES, -} from '../../../constants'; -import { TitleInformation } from '../../../components'; -import { - isFormEditing, - memoizeValidation, -} from '../../../utils'; - -import css from './InstanceInformation.css'; - -export const INSTANCE_SEGMENT_FOR_PLUGIN = 'instances'; - -class InstanceInformation extends Component { - static propTypes = { - triggerValidation: PropTypes.func.isRequired, - findInstance: PropTypes.func.isRequired, - submitting: PropTypes.bool.isRequired, - form: PropTypes.object.isRequired, - values: PropTypes.object.isRequired, - onSetSelectedInstance: PropTypes.func.isRequired, - isLoading: PropTypes.bool.isRequired, - instanceId: PropTypes.string.isRequired, - request: PropTypes.object, - instanceRequestCount: PropTypes.number, - selectedInstance: PropTypes.object, - }; - - constructor(props) { - super(props); - - this.state = { - shouldValidateId: false, - isInstanceClicked: false, - isInstanceBlurred: false, - validatedId: null, - }; - } - - validate = memoizeValidation(async (instanceId) => { - const { - selectedInstance, - findInstance, - } = this.props; - const { shouldValidateId } = this.state; - - if (!instanceId || (!instanceId && !selectedInstance?.id)) { - return ; - } - - if (instanceId && shouldValidateId) { - this.setState({ shouldValidateId: false }); - - const instance = await findInstance(instanceId, null, true); - - return !instance - ? - : undefined; - } - - return undefined; - }); - - handleChange = (event) => { - const { form } = this.props; - const { - isInstanceClicked, - isInstanceBlurred, - validatedId, - } = this.state; - const instanceId = event.target.value; - - if (isInstanceClicked || isInstanceBlurred) { - this.setState({ - isInstanceClicked: false, - isInstanceBlurred: false, - }); - } - - if (!isNull(validatedId)) { - this.setState({ validatedId: null }); - } - - form.change(REQUEST_FORM_FIELD_NAMES.INSTANCE_HRID, instanceId); - }; - - handleBlur = (input) => () => { - const { triggerValidation } = this.props; - const { validatedId } = this.state; - - if (input.value && input.value !== validatedId) { - this.setState({ - shouldValidateId: true, - isInstanceBlurred: true, - validatedId: input.value, - }, () => { - input.onBlur(); - triggerValidation(); - }); - } else if (!input.value) { - input.onBlur(); - } - }; - - handleClick = (eventKey) => { - const { - values, - onSetSelectedInstance, - findInstance, - triggerValidation, - } = this.props; - const instanceId = values.instance?.hrid; - - if (instanceId) { - onSetSelectedInstance(null); - this.setState(({ - isInstanceClicked: true, - })); - findInstance(instanceId); - - if (eventKey === ENTER_EVENT_KEY) { - this.setState({ - shouldValidateId: true, - }, triggerValidation); - } - } - }; - - onKeyDown = (e) => { - if (e.key === ENTER_EVENT_KEY && !e.shiftKey) { - e.preventDefault(); - this.handleClick(e.key); - } - }; - - render() { - const { - request, - selectedInstance, - findInstance, - submitting, - values, - isLoading, - instanceRequestCount, - instanceId, - } = this.props; - const { - isInstanceClicked, - isInstanceBlurred, - } = this.state; - const isEditForm = isFormEditing(request); - const titleLevelRequestsCount = request?.titleRequestCount || instanceRequestCount; - const isTitleInfoVisible = selectedInstance && !isLoading; - - return ( - - - { - !isEditForm && - <> - - - - {placeholder => { - const key = values.keyOfInstanceIdField ?? 0; - - return ( - - {({ input, meta }) => { - const selectInstanceError = meta.touched && meta.error; - const instanceDoesntExistError = (isInstanceClicked || isInstanceBlurred) && meta.error; - const error = selectInstanceError || instanceDoesntExistError || null; - - return ( - } - error={error} - onChange={this.handleChange} - onBlur={this.handleBlur(input)} - onKeyDown={this.onKeyDown} - /> - ); - }} - - ); - }} - - - - - - - - - } - selectInstance={(instanceFromPlugin) => findInstance(instanceFromPlugin.hrid)} - config={{ - availableSegments: [{ - name: INSTANCE_SEGMENT_FOR_PLUGIN, - }], - }} - /> - - - - } - { - isLoading && - } - { - isTitleInfoVisible && - - } - - - ); - } -} - -export default InstanceInformation; diff --git a/src/deprecated/components/InstanceInformation/InstanceInformation.test.js b/src/deprecated/components/InstanceInformation/InstanceInformation.test.js deleted file mode 100644 index e5fd6abf..00000000 --- a/src/deprecated/components/InstanceInformation/InstanceInformation.test.js +++ /dev/null @@ -1,671 +0,0 @@ -import { useState } from 'react'; -import { Field } from 'react-final-form'; - -import { - render, - screen, - fireEvent, - cleanup, - waitFor, -} from '@folio/jest-config-stripes/testing-library/react'; - -import { - Icon, - TextField, -} from '@folio/stripes/components'; -import { Pluggable } from '@folio/stripes/core'; - -import InstanceInformation, { - INSTANCE_SEGMENT_FOR_PLUGIN, -} from './InstanceInformation'; -import { TitleInformation } from '../../../components'; -import { isFormEditing } from '../../../utils'; -import { - BASE_SPINNER_PROPS, - ENTER_EVENT_KEY, - REQUEST_FORM_FIELD_NAMES, -} from '../../../constants'; - -jest.mock('../../../utils', () => ({ - memoizeValidation: (fn) => () => fn, - isFormEditing: jest.fn(() => false), -})); -jest.mock('../../../components', () => ({ - TitleInformation: jest.fn(() =>
TitleInformation
), -})); - -const basicProps = { - triggerValidation: jest.fn(), - findInstance: jest.fn(() => null), - onSetSelectedInstance: jest.fn(), - form: { - change: jest.fn(), - }, - values: { - instance: { - hrid: 'hrid', - }, - keyOfInstanceIdField: 1, - }, - request: { - id: 'requestId', - }, - selectedInstance: { - title: 'instance title', - contributors: {}, - publication: {}, - editions: {}, - identifiers: {}, - }, - instanceRequestCount: 1, - instanceId: 'instanceId', - isLoading: false, - submitting: false, -}; -const labelIds = { - scanOrEnterBarcode: 'ui-requests.instance.scanOrEnterBarcode', - instanceHrid: 'ui-requests.instance.value', - enterButton: 'ui-requests.enter', - selectInstanceRequired: 'ui-requests.errors.selectInstanceRequired', - instanceUuidOrHridDoesNotExist: 'ui-requests.errors.instanceUuidOrHridDoesNotExist', - titleLookupPlugin: 'ui-requests.titleLookupPlugin', -}; -const testIds = { - instanceHridField: 'instanceHridField', - errorMessage: 'errorMessage', -}; -const renderInstanceInfoWithHrid = (onBlur) => { - Field.mockImplementation(jest.fn(({ - children, - 'data-testid': testId, - validate, - }) => { - return children({ - meta: {}, - input: { - validate, - 'data-testid': testId, - value: 'hrid', - onBlur, - }, - }); - })); - - render( - - ); -}; - -describe('InstanceInformation', () => { - afterEach(() => { - basicProps.onSetSelectedInstance.mockClear(); - Field.mockClear(); - cleanup(); - }); - - describe('when "isFormEditing" returns false', () => { - beforeEach(() => { - render( - - ); - }); - - it('should render "scanOrEnterBarcode" placeholder', () => { - const scanOrEnterBarcodePlaceholder = screen.getByPlaceholderText(labelIds.scanOrEnterBarcode); - - expect(scanOrEnterBarcodePlaceholder).toBeVisible(); - }); - - it('should render instance hrid "Field" with correct props', () => { - const expectedProps = { - name: REQUEST_FORM_FIELD_NAMES.INSTANCE_HRID, - validate: expect.any(Function), - validateFields: [], - }; - - expect(Field).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - - it('should render instance hrid label', () => { - const instanceHridLabel = screen.getByText(labelIds.instanceHrid); - - expect(instanceHridLabel).toBeVisible(); - }); - - it('should trigger "findInstance" when Enter key is pressed', () => { - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - fireEvent.keyDown(instanceHridField, { key: ENTER_EVENT_KEY }); - - expect(basicProps.findInstance).toHaveBeenCalledWith(basicProps.values.instance.hrid); - }); - - it('should not trigger "findInstance" when Control key is pressed', () => { - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - fireEvent.keyDown(instanceHridField, { key: 'Control' }); - - expect(basicProps.findInstance).not.toHaveBeenCalledWith(); - }); - - it('should trigger "form.change" with correct arguments', () => { - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - const event = { - target: { - value: 'instanceHrid', - }, - }; - - fireEvent.change(instanceHridField, event); - - expect(basicProps.form.change).toHaveBeenCalledWith(REQUEST_FORM_FIELD_NAMES.INSTANCE_HRID, event.target.value); - }); - - it('should render "TextField" with correct props', () => { - const expectedProps = { - required: true, - error: null, - placeholder: [labelIds.scanOrEnterBarcode], - onChange: expect.any(Function), - onBlur: expect.any(Function), - onKeyDown: expect.any(Function), - }; - - expect(TextField).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - - it('should render "TextField" with validation error', () => { - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - const enterButton = screen.getByText(labelIds.enterButton); - const event = { - target: { - value: 'instanceHrid', - }, - }; - const error = 'error'; - - Field.mockImplementationOnce(jest.fn(({ - children, - 'data-testid': testId, - validate, - }) => { - return children({ - meta: { - error, - touched: true, - }, - input: { - validate, - 'data-testid': testId, - }, - }); - })); - - fireEvent.click(enterButton); - fireEvent.change(instanceHridField, event); - - expect(TextField).toHaveBeenCalledWith(expect.objectContaining({ error }), {}); - }); - - it('should render "Pluggable" with correct props', () => { - const expectedProps = { - searchButtonStyle: 'link', - type: 'find-instance', - selectInstance: expect.any(Function), - config: { - availableSegments: [{ - name: INSTANCE_SEGMENT_FOR_PLUGIN, - }], - }, - }; - - expect(Pluggable).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - - it('should render title lookup plugin label', () => { - const titleLookupPluginLabel = screen.getByText(labelIds.titleLookupPlugin); - - expect(titleLookupPluginLabel).toBeVisible(); - }); - - it('should trigger "findInstance" with correct argument', () => { - const hrid = 'hrid'; - const searchButtonLabel = 'Search'; - const searchButton = screen.getByText(searchButtonLabel); - - fireEvent.click(searchButton); - - expect(basicProps.findInstance).toHaveBeenCalledWith(hrid); - }); - }); - - describe('when "isFormEditing" returns true', () => { - beforeEach(() => { - isFormEditing.mockReturnValueOnce(true); - - render( - - ); - }); - - it('should not render "scanOrEnterBarcode" placeholder', () => { - const scanOrEnterBarcodePlaceholder = screen.queryByPlaceholderText(labelIds.scanOrEnterBarcode); - - expect(scanOrEnterBarcodePlaceholder).not.toBeInTheDocument(); - }); - - it('should not render instance hrid field', () => { - const instanceHridField = screen.queryByTestId(testIds.instanceHridField); - - expect(instanceHridField).not.toBeInTheDocument(); - }); - - it('should not render instance hrid label', () => { - const instanceHridLabel = screen.queryByText(labelIds.instanceHrid); - - expect(instanceHridLabel).not.toBeInTheDocument(); - }); - }); - - describe('handleBlur', () => { - const onBlur = jest.fn(); - - afterEach(() => { - onBlur.mockClear(); - }); - - it('should trigger "input.onBlur" if instance hrid is presented', () => { - renderInstanceInfoWithHrid(onBlur); - - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - fireEvent.click(instanceHridField); - fireEvent.blur(instanceHridField); - - expect(onBlur).toHaveBeenCalled(); - }); - - it('should trigger "input.onBlur" if there is no instance hrid', () => { - Field.mockImplementation(jest.fn(({ - children, - 'data-testid': testId, - validate, - }) => { - return children({ - meta: {}, - input: { - validate, - 'data-testid': testId, - value: '', - onBlur, - }, - }); - })); - - render( - - ); - - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - fireEvent.click(instanceHridField); - fireEvent.blur(instanceHridField); - - expect(onBlur).toHaveBeenCalled(); - }); - - it('should not trigger "input.onBlur" if instance hrid was validated previously', () => { - renderInstanceInfoWithHrid(onBlur); - - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - // first input focus - fireEvent.click(instanceHridField); - fireEvent.blur(instanceHridField); - onBlur.mockClear(); - - // second input focus after validation of initial value - fireEvent.click(instanceHridField); - fireEvent.blur(instanceHridField); - - expect(onBlur).not.toHaveBeenCalled(); - }); - }); - - describe('Validation', () => { - afterEach(() => { - basicProps.findInstance.mockClear(); - TextField.mockClear(); - }); - - beforeEach(() => { - TextField.mockImplementation(({ - onChange, - validate, - ...rest - }) => { - const [error, setError] = useState(''); - const handleChange = async (e) => { - setError(await validate(e.target.value)); - onChange(e); - }; - - return ( -
- - {error} -
- ); - }); - }); - - describe('when instance hrid is not presented', () => { - const event = { - target: { - value: '', - }, - }; - - it('should not render error message', async () => { - render( - - ); - - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - fireEvent.change(instanceHridField, event); - - await waitFor(() => { - const errorMessage = screen.getByTestId(testIds.errorMessage); - - expect(errorMessage).toBeEmpty(); - }); - }); - - it('should render "selectInstanceRequired" error message', async () => { - const props = { - ...basicProps, - selectedInstance: { - id: 'hrid', - }, - }; - - render( - - ); - - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - fireEvent.change(instanceHridField, event); - - await waitFor(() => { - const errorMessage = screen.queryByText(labelIds.selectInstanceRequired); - - expect(errorMessage).toBeVisible(); - }); - }); - }); - - describe('when instance hrid is presented', () => { - const event = { - target: { - value: 'instanceId', - }, - }; - - beforeEach(() => { - render( - - ); - }); - - it('should not render error message', async () => { - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - fireEvent.change(instanceHridField, event); - - await waitFor(() => { - const errorMessage = screen.getByTestId(testIds.errorMessage); - - expect(errorMessage).toBeEmpty(); - }); - }); - - it('should render "instanceUuidOrHridDoesNotExist" error message', async () => { - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - fireEvent.keyDown(instanceHridField, { key: ENTER_EVENT_KEY }); - fireEvent.change(instanceHridField, event); - - await waitFor(() => { - const errorMessage = screen.queryByText(labelIds.instanceUuidOrHridDoesNotExist); - - expect(errorMessage).toBeVisible(); - }); - }); - - it('should not render error message if instance found', async () => { - const instanceHridField = screen.getByTestId(testIds.instanceHridField); - - basicProps.findInstance.mockReturnValue({}); - fireEvent.keyDown(instanceHridField, { key: ENTER_EVENT_KEY }); - fireEvent.change(instanceHridField, event); - - await waitFor(() => { - const errorMessage = screen.getByTestId(testIds.errorMessage); - - expect(errorMessage).toBeEmpty(); - }); - }); - }); - }); - - describe('"Enter" button', () => { - describe('when instance hrid is presented', () => { - beforeEach(() => { - render( - - ); - }); - - it('should render "Enter" button', () => { - const enterButton = screen.getByText(labelIds.enterButton); - - expect(enterButton).toBeVisible(); - }); - - it('should trigger "onSetSelectedInstance" with correct argument', () => { - const enterButton = screen.getByText(labelIds.enterButton); - - fireEvent.click(enterButton); - - expect(basicProps.onSetSelectedInstance).toHaveBeenCalledWith(null); - }); - - it('should trigger "findInstance" with correct argument', () => { - const enterButton = screen.getByText(labelIds.enterButton); - - fireEvent.click(enterButton); - - expect(basicProps.findInstance).toHaveBeenCalledWith(basicProps.values.instance.hrid); - }); - }); - - describe('when instance hrid is not presented', () => { - const props = { - ...basicProps, - values: { - instance: { - hrid: '', - }, - }, - }; - - beforeEach(() => { - render( - - ); - }); - - it('should not trigger "onSetSelectedInstance"', () => { - const enterButton = screen.getByText(labelIds.enterButton); - - fireEvent.click(enterButton); - - expect(basicProps.onSetSelectedInstance).not.toHaveBeenCalled(); - }); - }); - }); - - describe('Spinner', () => { - afterEach(() => { - Icon.mockClear(); - }); - - describe('when data is loading', () => { - const props = { - ...basicProps, - isLoading: true, - }; - - beforeEach(() => { - render( - - ); - }); - - it('should render loading "Icon" with correct props', () => { - expect(Icon).toHaveBeenCalledWith(BASE_SPINNER_PROPS, {}); - }); - }); - - describe('when data is not loading', () => { - beforeEach(() => { - render( - - ); - }); - - it('should not render loading "Icon"', () => { - expect(Icon).not.toHaveBeenCalled(); - }); - }); - }); - - describe('TitleInformation', () => { - afterEach(() => { - TitleInformation.mockClear(); - }); - - describe('when instance is selected', () => { - it('should render "TitleInformation" with correct props', () => { - render( - - ); - - const expectedProps = { - instanceId: basicProps.instanceId, - titleLevelRequestsCount: basicProps.instanceRequestCount, - title: basicProps.selectedInstance.title, - contributors: basicProps.selectedInstance.contributors, - publications: basicProps.selectedInstance.publication, - editions: basicProps.selectedInstance.editions, - identifiers: basicProps.selectedInstance.identifiers, - }; - - expect(TitleInformation).toHaveBeenCalledWith(expectedProps, {}); - }); - - it('should render "TitleInformation" with "request.instanceId"', () => { - const instanceId = 'instanceId'; - const props = { - ...basicProps, - request: { - ...basicProps.request, - instanceId, - }, - }; - const expectedProps = { - instanceId, - }; - - render( - - ); - - expect(TitleInformation).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - - it('should render "TitleInformation" with "selectedInstance.id"', () => { - const selectedInstanceId = 'selectedInstanceId'; - const props = { - ...basicProps, - selectedInstance: { - ...basicProps.selectedInstance, - id: selectedInstanceId, - }, - }; - const expectedProps = { - instanceId: selectedInstanceId, - }; - - render( - - ); - - expect(TitleInformation).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - }); - - describe('when instance is not selected', () => { - const props = { - ...basicProps, - selectedInstance: undefined, - }; - - beforeEach(() => { - render( - - ); - }); - - it('should not render "TitleInformation"', () => { - expect(TitleInformation).not.toHaveBeenCalled(); - }); - }); - }); -}); diff --git a/src/deprecated/components/ItemInformation/ItemInformation.css b/src/deprecated/components/ItemInformation/ItemInformation.css deleted file mode 100644 index 97c034ab..00000000 --- a/src/deprecated/components/ItemInformation/ItemInformation.css +++ /dev/null @@ -1,3 +0,0 @@ -.enterButton { - margin-top: 25px; -} diff --git a/src/deprecated/components/ItemInformation/ItemInformation.js b/src/deprecated/components/ItemInformation/ItemInformation.js deleted file mode 100644 index 58e517e3..00000000 --- a/src/deprecated/components/ItemInformation/ItemInformation.js +++ /dev/null @@ -1,250 +0,0 @@ -import { Component } from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; -import { Field } from 'react-final-form'; -import { isNull } from 'lodash'; - -import { - Button, - Col, - Icon, - Row, - TextField, -} from '@folio/stripes/components'; - -import { - REQUEST_FORM_FIELD_NAMES, - RESOURCE_KEYS, - ENTER_EVENT_KEY, - BASE_SPINNER_PROPS, -} from '../../../constants'; -import ItemDetail from '../../../ItemDetail'; -import { - isFormEditing, - memoizeValidation, -} from '../../../utils'; - -import css from './ItemInformation.css'; - -class ItemInformation extends Component { - static propTypes = { - triggerValidation: PropTypes.func.isRequired, - findItem: PropTypes.func.isRequired, - form: PropTypes.object.isRequired, - values: PropTypes.object.isRequired, - request: PropTypes.object.isRequired, - onSetSelectedItem: PropTypes.func.isRequired, - itemRequestCount: PropTypes.number.isRequired, - instanceId: PropTypes.string.isRequired, - isLoading: PropTypes.bool.isRequired, - submitting: PropTypes.bool.isRequired, - isItemIdRequest: PropTypes.bool.isRequired, - selectedLoan: PropTypes.object, - selectedItem: PropTypes.object, - }; - - constructor(props) { - super(props); - - this.state = { - shouldValidateBarcode: false, - isItemClicked: false, - isItemBlurred: false, - validatedBarcode: null, - }; - } - - validate = memoizeValidation(async (barcode) => { - const { - isItemIdRequest, - findItem, - } = this.props; - const { shouldValidateBarcode } = this.state; - - if (isItemIdRequest && !barcode) { - return undefined; - } - - if (!barcode && !isItemIdRequest) { - return ; - } - - if (barcode && shouldValidateBarcode) { - this.setState({ shouldValidateBarcode: false }); - - const item = await findItem(RESOURCE_KEYS.barcode, barcode, true); - - return !item - ? - : undefined; - } - - return undefined; - }); - - handleChange = (event) => { - const { form } = this.props; - const { - isItemClicked, - isItemBlurred, - validatedBarcode, - } = this.state; - const barcode = event.target.value; - - if (isItemClicked || isItemBlurred) { - this.setState({ - isItemClicked: false, - isItemBlurred: false, - }); - } - - if (!isNull(validatedBarcode)) { - this.setState({ validatedBarcode: null }); - } - - form.change(REQUEST_FORM_FIELD_NAMES.ITEM_BARCODE, barcode); - }; - - handleBlur = (input) => () => { - const { triggerValidation } = this.props; - const { validatedBarcode } = this.state; - - if (input.value && input.value !== validatedBarcode) { - this.setState({ - shouldValidateBarcode: true, - isItemBlurred: true, - validatedBarcode: input.value, - }, () => { - input.onBlur(); - triggerValidation(); - }); - } else if (!input.value) { - input.onBlur(); - } - } - - onKeyDown = (e) => { - if (e.key === ENTER_EVENT_KEY && !e.shiftKey) { - e.preventDefault(); - this.handleClick(e.key); - } - }; - - handleClick = (eventKey) => { - const { - onSetSelectedItem, - findItem, - triggerValidation, - values, - } = this.props; - const barcode = values.item?.barcode; - - if (barcode) { - onSetSelectedItem(null); - this.setState(({ - isItemClicked: true, - })); - - findItem(RESOURCE_KEYS.barcode, barcode); - - if (eventKey === ENTER_EVENT_KEY) { - this.setState({ - shouldValidateBarcode: true, - }, triggerValidation); - } - } - } - - render() { - const { - values, - submitting, - isLoading, - selectedItem, - request, - instanceId, - selectedLoan, - itemRequestCount, - } = this.props; - const { - isItemClicked, - isItemBlurred, - } = this.state; - const isEditForm = isFormEditing(request); - - return ( - - - { - !isEditForm && - - - - {placeholder => { - const key = values.keyOfItemBarcodeField ?? 0; - - return ( - - {({ input, meta }) => { - const selectItemError = meta.touched && meta.error; - const itemDoesntExistError = (isItemClicked || isItemBlurred) && meta.error; - const error = meta.submitError || selectItemError || itemDoesntExistError || null; - - return ( - } - error={error} - onChange={this.handleChange} - onBlur={this.handleBlur(input)} - onKeyDown={this.onKeyDown} - /> - ); - }} - - ); - }} - - - - - - - } - { - isLoading && - } - { - selectedItem && - - } - - - ); - } -} - -export default ItemInformation; diff --git a/src/deprecated/components/ItemInformation/ItemInformation.test.js b/src/deprecated/components/ItemInformation/ItemInformation.test.js deleted file mode 100644 index 5e6a7347..00000000 --- a/src/deprecated/components/ItemInformation/ItemInformation.test.js +++ /dev/null @@ -1,589 +0,0 @@ -import { useState } from 'react'; -import { Field } from 'react-final-form'; - -import { - render, - screen, - fireEvent, - cleanup, - waitFor, -} from '@folio/jest-config-stripes/testing-library/react'; -import { - Icon, - TextField, -} from '@folio/stripes/components'; - -import ItemInformation from './ItemInformation'; -import ItemDetail from '../../../ItemDetail'; -import { isFormEditing } from '../../../utils'; -import { - REQUEST_FORM_FIELD_NAMES, - RESOURCE_KEYS, - ENTER_EVENT_KEY, - BASE_SPINNER_PROPS, -} from '../../../constants'; - -jest.mock('../../../utils', () => ({ - isFormEditing: jest.fn(() => false), - memoizeValidation: (fn) => () => fn, -})); -jest.mock('../../../ItemDetail', () => jest.fn(() =>
Item Details
)); - -const basicProps = { - triggerValidation: jest.fn(), - findItem: jest.fn(() => null), - onSetSelectedItem: jest.fn(), - form: { - change: jest.fn(), - }, - values: { - item: { - barcode: 'itemBarcode', - }, - keyOfItemBarcodeField: 1, - }, - request: { - id: 'requestId', - }, - selectedLoan: {}, - selectedItem: {}, - itemRequestCount: 1, - instanceId: 'instanceId', - isLoading: false, - submitting: false, - isItemIdRequest: true, -}; -const labelIds = { - scanOrEnterBarcode: 'ui-requests.item.scanOrEnterBarcode', - itemBarcode: 'ui-requests.item.barcode', - enterButton: 'ui-requests.enter', - selectItemRequired: 'ui-requests.errors.selectItemRequired', - itemBarcodeDoesNotExist: 'ui-requests.errors.itemBarcodeDoesNotExist', -}; -const testIds = { - itemBarcodeField: 'itemBarcodeField', - errorMessage: 'errorMessage', -}; -const renderItemInfoWithBarcode = (onBlur) => { - Field.mockImplementation(jest.fn(({ - children, - 'data-testid': testId, - validate, - }) => { - return children({ - meta: {}, - input: { - validate, - 'data-testid': testId, - value: 'itemBarcode', - onBlur, - }, - }); - })); - - render( - - ); -}; - -describe('ItemInformation', () => { - afterEach(() => { - basicProps.onSetSelectedItem.mockClear(); - Field.mockClear(); - cleanup(); - }); - - describe('when "isFormEditing" returns false', () => { - beforeEach(() => { - render( - - ); - }); - - it('should render "scanOrEnterBarcode" placeholder', () => { - const scanOrEnterBarcodePlaceholder = screen.getByPlaceholderText(labelIds.scanOrEnterBarcode); - - expect(scanOrEnterBarcodePlaceholder).toBeVisible(); - }); - - it('should render item barcode field', () => { - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - expect(itemBarcodeField).toBeVisible(); - }); - - it('should render item barcode "Field" with correct props', () => { - const expectedProps = { - name: REQUEST_FORM_FIELD_NAMES.ITEM_BARCODE, - validate: expect.any(Function), - validateFields: [], - }; - - expect(Field).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - - it('should render item barcode label', () => { - const itemBarcodeLabel = screen.getByText(labelIds.itemBarcode); - - expect(itemBarcodeLabel).toBeVisible(); - }); - - it('should trigger "findItem" when Enter key is pressed', () => { - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - fireEvent.keyDown(itemBarcodeField, { key: ENTER_EVENT_KEY }); - - expect(basicProps.findItem).toHaveBeenCalledWith(RESOURCE_KEYS.barcode, basicProps.values.item.barcode); - }); - - it('should not trigger "findItem" when Control key is pressed', () => { - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - fireEvent.keyDown(itemBarcodeField, { key: 'Control' }); - - expect(basicProps.findItem).not.toHaveBeenCalledWith(); - }); - - it('should trigger "form.change" with correct arguments', () => { - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - const event = { - target: { - value: 'itemBarcode', - }, - }; - - fireEvent.change(itemBarcodeField, event); - - expect(basicProps.form.change).toHaveBeenCalledWith(REQUEST_FORM_FIELD_NAMES.ITEM_BARCODE, event.target.value); - }); - - it('should render "TextField" with correct props', () => { - const expectedProps = { - required: true, - error: null, - onChange: expect.any(Function), - onBlur: expect.any(Function), - onKeyDown: expect.any(Function), - }; - - expect(TextField).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - - it('should render "TextField" with validation error', () => { - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - const enterButton = screen.getByText(labelIds.enterButton); - const event = { - target: { - value: 'itemBarcode', - }, - }; - const error = 'error'; - - Field.mockImplementationOnce(jest.fn(({ - children, - 'data-testid': testId, - validate, - }) => { - return children({ - meta: { - error: 'error', - touched: true, - }, - input: { - validate, - 'data-testid': testId, - }, - }); - })); - - fireEvent.click(enterButton); - fireEvent.change(itemBarcodeField, event); - - expect(TextField).toHaveBeenCalledWith(expect.objectContaining({ error }), {}); - }); - }); - - describe('when "isFormEditing" returns true', () => { - beforeEach(() => { - isFormEditing.mockReturnValueOnce(true); - - render( - - ); - }); - - it('should not render "scanOrEnterBarcode" placeholder', () => { - const scanOrEnterBarcodePlaceholder = screen.queryByPlaceholderText(labelIds.scanOrEnterBarcode); - - expect(scanOrEnterBarcodePlaceholder).not.toBeInTheDocument(); - }); - - it('should not render item barcode field', () => { - const itemBarcodeField = screen.queryByTestId(testIds.itemBarcodeField); - - expect(itemBarcodeField).not.toBeInTheDocument(); - }); - - it('should not render item barcode label', () => { - const itemBarcodeLabel = screen.queryByText(labelIds.itemBarcode); - - expect(itemBarcodeLabel).not.toBeInTheDocument(); - }); - }); - - describe('handleBlur', () => { - const onBlur = jest.fn(); - - afterEach(() => { - onBlur.mockClear(); - }); - - it('should trigger "input.onBlur" if item barcode is presented', () => { - renderItemInfoWithBarcode(onBlur); - - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - fireEvent.click(itemBarcodeField); - fireEvent.blur(itemBarcodeField); - - expect(onBlur).toHaveBeenCalled(); - }); - - it('should trigger "input.onBlur" if there is no item barcode', () => { - Field.mockImplementation(jest.fn(({ - children, - 'data-testid': testId, - validate, - }) => { - return children({ - meta: {}, - input: { - validate, - 'data-testid': testId, - value: '', - onBlur, - }, - }); - })); - - render( - - ); - - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - fireEvent.click(itemBarcodeField); - fireEvent.blur(itemBarcodeField); - - expect(onBlur).toHaveBeenCalled(); - }); - - it('should not trigger "input.onBlur" if item barcode was validated previously', () => { - renderItemInfoWithBarcode(onBlur); - - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - // first input focus - fireEvent.click(itemBarcodeField); - fireEvent.blur(itemBarcodeField); - onBlur.mockClear(); - - // second input focus after validation of initial value - fireEvent.click(itemBarcodeField); - fireEvent.blur(itemBarcodeField); - - expect(onBlur).not.toHaveBeenCalled(); - }); - }); - - describe('Validation', () => { - afterEach(() => { - TextField.mockClear(); - basicProps.findItem.mockClear(); - }); - - beforeEach(() => { - TextField.mockImplementation(jest.fn(({ - onChange, - validate, - ...rest - }) => { - const [error, setError] = useState(''); - const handleChange = async (e) => { - setError(await validate(e.target.value)); - onChange(e); - }; - - return ( -
- - {error} -
- ); - })); - }); - - describe('when "barcode" is not presented', () => { - const event = { - target: { - value: '', - }, - }; - - it('should not render error message', async () => { - render( - - ); - - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - fireEvent.change(itemBarcodeField, event); - - await waitFor(() => { - const errorMessage = screen.getByTestId(testIds.errorMessage); - - expect(errorMessage).toBeEmpty(); - }); - }); - - it('should render "selectItemRequired" error message', async () => { - const props = { - ...basicProps, - isItemIdRequest: false, - }; - - render( - - ); - - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - fireEvent.change(itemBarcodeField, event); - - await waitFor(() => { - const errorMessage = screen.queryByText(labelIds.selectItemRequired); - - expect(errorMessage).toBeVisible(); - }); - }); - }); - - describe('when "barcode" is presented', () => { - const event = { - target: { - value: 'barcode', - }, - }; - - beforeEach(() => { - render( - - ); - }); - - it('should not render error message', async () => { - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - fireEvent.change(itemBarcodeField, event); - - await waitFor(() => { - const errorMessage = screen.getByTestId(testIds.errorMessage); - - expect(errorMessage).toBeEmpty(); - }); - }); - - it('should render "itemBarcodeDoesNotExist" error message', async () => { - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - fireEvent.keyDown(itemBarcodeField, { key: ENTER_EVENT_KEY }); - fireEvent.change(itemBarcodeField, event); - - await waitFor(() => { - const errorMessage = screen.queryByText(labelIds.itemBarcodeDoesNotExist); - - expect(errorMessage).toBeVisible(); - }); - }); - - it('should not render error message if item found', async () => { - const itemBarcodeField = screen.getByTestId(testIds.itemBarcodeField); - - basicProps.findItem.mockReturnValue({}); - fireEvent.keyDown(itemBarcodeField, { key: ENTER_EVENT_KEY }); - fireEvent.change(itemBarcodeField, event); - - await waitFor(() => { - const errorMessage = screen.getByTestId(testIds.errorMessage); - - expect(errorMessage).toBeEmpty(); - }); - }); - }); - }); - - describe('"Enter" button', () => { - describe('when barcode is presented', () => { - beforeEach(() => { - render( - - ); - }); - - it('should render "Enter" button', () => { - const enterButton = screen.getByText(labelIds.enterButton); - - expect(enterButton).toBeVisible(); - }); - - it('should trigger "onSetSelectedItem" with correct argument', () => { - const enterButton = screen.getByText(labelIds.enterButton); - - fireEvent.click(enterButton); - - expect(basicProps.onSetSelectedItem).toHaveBeenCalledWith(null); - }); - - it('should trigger "findItem" with correct arguments', () => { - const enterButton = screen.getByText(labelIds.enterButton); - - fireEvent.click(enterButton); - - expect(basicProps.findItem).toHaveBeenCalledWith(RESOURCE_KEYS.barcode, basicProps.values.item.barcode); - }); - }); - - describe('when barcode is not presented', () => { - const props = { - ...basicProps, - values: { - item: { - barcode: '', - }, - }, - }; - - beforeEach(() => { - render( - - ); - }); - - it('should not trigger "onSetSelectedItem"', () => { - const enterButton = screen.getByText(labelIds.enterButton); - - fireEvent.click(enterButton); - - expect(basicProps.onSetSelectedItem).not.toHaveBeenCalled(); - }); - }); - }); - - describe('Spinner', () => { - afterEach(() => { - Icon.mockClear(); - }); - - describe('when data is loading', () => { - const props = { - ...basicProps, - isLoading: true, - }; - - beforeEach(() => { - render( - - ); - }); - - it('should render loading "Icon" with correct props', () => { - expect(Icon).toHaveBeenCalledWith(BASE_SPINNER_PROPS, {}); - }); - }); - - describe('when data is not loading', () => { - beforeEach(() => { - render( - - ); - }); - - it('should not render loading "Icon"', () => { - expect(Icon).not.toHaveBeenCalled(); - }); - }); - }); - - describe('ItemDetails', () => { - afterEach(() => { - ItemDetail.mockClear(); - }); - - describe('when item is selected', () => { - beforeEach(() => { - render( - - ); - }); - - it('should render "ItemDetail" with correct props', () => { - const expectedProps = { - request: basicProps.request, - currentInstanceId: basicProps.instanceId, - item: basicProps.selectedItem, - loan: basicProps.selectedLoan, - requestCount: basicProps.itemRequestCount, - }; - - expect(ItemDetail).toHaveBeenCalledWith(expectedProps, {}); - }); - }); - - describe('when item is not selected', () => { - const props = { - ...basicProps, - selectedItem: undefined, - }; - - beforeEach(() => { - render( - - ); - }); - - it('should not render "ItemDetail"', () => { - expect(ItemDetail).not.toHaveBeenCalled(); - }); - }); - }); -}); diff --git a/src/deprecated/components/ItemsDialog/ItemsDialog.css b/src/deprecated/components/ItemsDialog/ItemsDialog.css deleted file mode 100644 index 00fa56d4..00000000 --- a/src/deprecated/components/ItemsDialog/ItemsDialog.css +++ /dev/null @@ -1,4 +0,0 @@ -.content { - padding: 0px; - margin: 0px; -} diff --git a/src/deprecated/components/ItemsDialog/ItemsDialog.js b/src/deprecated/components/ItemsDialog/ItemsDialog.js deleted file mode 100644 index 054b29ac..00000000 --- a/src/deprecated/components/ItemsDialog/ItemsDialog.js +++ /dev/null @@ -1,283 +0,0 @@ -import React, { - useState, - useLayoutEffect, - useMemo, -} from 'react'; -import PropTypes from 'prop-types'; -import { - get, - noop, - countBy, - chunk, -} from 'lodash'; -import { - useIntl, - FormattedMessage, -} from 'react-intl'; - -import { - Modal, - MultiColumnList, - Pane, - Paneset, -} from '@folio/stripes/components'; -import { stripesConnect } from '@folio/stripes/core'; - -import { - itemStatuses, - itemStatusesTranslations, - requestableItemStatuses, - MAX_RECORDS, - OPEN_REQUESTS_STATUSES, -} from '../../../constants'; -import { Loading } from '../../../components'; -import { getStatusQuery } from '../../../routes/utils'; - -import css from './ItemsDialog.css'; - -export const COLUMN_NAMES = [ - 'barcode', - 'itemStatus', - 'requestQueue', - 'location', - 'materialType', - 'loanType', -]; - -export const COLUMN_WIDTHS = { - barcode: '16%', - itemStatus: '16%', - requestQueue: '16%', - location: '16%', - materialType: '16%', - loanType: '16%', -}; - -export const COLUMN_MAP = { - barcode: , - itemStatus: , - requestQueue: , - location: , - materialType: , - loanType: , -}; - -export const formatter = { - itemStatus: item => , - location: item => get(item, 'effectiveLocation.name', ''), - materialType: item => item.materialType.name, - loanType: item => (item.temporaryLoanType ? get(item, 'temporaryLoanType.name', '') : get(item, 'permanentLoanType.name', '')), -}; - -export const MAX_HEIGHT = 500; -const CHUNK_SIZE = 40; - -const ItemsDialog = ({ - onClose, - open, - isLoading, - onRowClick = noop, - mutator, - skippedItemId, - title, - instanceId, -}) => { - const [areItemsBeingLoaded, setAreItemsBeingLoaded] = useState(false); - const [items, setItems] = useState([]); - const { formatMessage } = useIntl(); - - const fetchHoldings = () => { - const query = `instanceId==${instanceId}`; - mutator.holdings.reset(); - - return mutator.holdings.GET({ params: { query, limit: MAX_RECORDS } }); - }; - - const fetchItems = async (holdings) => { - const chunkedItems = chunk(holdings, CHUNK_SIZE); - const data = []; - - for (const itemChunk of chunkedItems) { - const query = itemChunk.map(i => `holdingsRecordId==${i.id}`).join(' or '); - - mutator.items.reset(); - // eslint-disable-next-line no-await-in-loop - const result = await mutator.items.GET({ params: { query, limit: MAX_RECORDS } }); - - data.push(...result); - } - - return data; - }; - - const fetchRequests = async (itemsList) => { - // Split the list of items into small chunks to create a short enough query string - // that we can avoid a "414 Request URI Too Long" response from Okapi. - const chunkedItems = chunk(itemsList, CHUNK_SIZE); - const data = []; - - for (const itemChunk of chunkedItems) { - let query = itemChunk.map(i => `itemId==${i.id}`).join(' or '); - const statusQuery = getStatusQuery(OPEN_REQUESTS_STATUSES); - - query = `(${query}) and (${statusQuery})")`; - - mutator.requests.reset(); - // eslint-disable-next-line no-await-in-loop - const result = await mutator.requests.GET({ params: { query, limit: MAX_RECORDS } }); - - data.push(...result); - } - - return data; - }; - - useLayoutEffect(() => { - const getItems = async () => { - setAreItemsBeingLoaded(true); - - const holdings = await fetchHoldings(); - let itemsList = await fetchItems(holdings); - - if (skippedItemId) { - itemsList = itemsList.filter(item => requestableItemStatuses.includes(item.status?.name)); - } - - const requests = await fetchRequests(itemsList); - const requestMap = countBy(requests, 'itemId'); - - itemsList = itemsList.map(item => ({ ...item, requestQueue: requestMap[item.id] || 0 })); - - setAreItemsBeingLoaded(false); - setItems(itemsList); - }; - - if (open && instanceId) { - getItems(); - } - - return () => setItems([]); - }, - // The deps react-hooks complains about here are the fetch* functions - // but both the suggestions (making them deps, moving them inside this - // function) cause test failures. I ... don't really understand the - // details of the problem, beyond the fact that it's obviously some kind - // of bad interaction between hooks and stripes-connect. - // - // eslint-disable-next-line react-hooks/exhaustive-deps - [instanceId, open]); - - const contentData = useMemo(() => { - let resultItems = items; - - if (skippedItemId) { - resultItems = resultItems - .filter(item => skippedItemId !== item.id); - } - - // items with status available must go first - resultItems.sort((a) => (a.status.name === itemStatuses.AVAILABLE ? -1 : 1)); - return resultItems.map(item => ({ - ...item, - status: { - ...item.status, - }, - })); - }, [items, skippedItemId]); - - const itemsAmount = contentData.length; - - return ( - - - - {isLoading || areItemsBeingLoaded - ? - : - } - - - - ); -}; - -ItemsDialog.manifest = { - holdings: { - type: 'okapi', - records: 'holdingsRecords', - path: 'holdings-storage/holdings', - accumulate: true, - fetch: false, - }, - items: { - type: 'okapi', - records: 'items', - path: 'inventory/items', - accumulate: true, - fetch: false, - }, - requests: { - type: 'okapi', - path: 'circulation/requests', - records: 'requests', - accumulate: true, - fetch: false, - }, -}; - -ItemsDialog.defaultProps = { - title: '', -}; - -ItemsDialog.propTypes = { - open: PropTypes.bool.isRequired, - onClose: PropTypes.func.isRequired, - isLoading: PropTypes.bool, - title: PropTypes.string, - instanceId: PropTypes.string, - skippedItemId: PropTypes.string, - onRowClick: PropTypes.func, - mutator: PropTypes.shape({ - holdings: PropTypes.shape({ - GET: PropTypes.func.isRequired, - reset: PropTypes.func.isRequired, - }).isRequired, - items: PropTypes.shape({ - GET: PropTypes.func.isRequired, - reset: PropTypes.func.isRequired, - }).isRequired, - requests: PropTypes.shape({ - GET: PropTypes.func.isRequired, - reset: PropTypes.func.isRequired, - }).isRequired, - }).isRequired, -}; - -export default stripesConnect(ItemsDialog); diff --git a/src/deprecated/components/ItemsDialog/ItemsDialog.test.js b/src/deprecated/components/ItemsDialog/ItemsDialog.test.js deleted file mode 100644 index ea2f2083..00000000 --- a/src/deprecated/components/ItemsDialog/ItemsDialog.test.js +++ /dev/null @@ -1,490 +0,0 @@ -import { - render, - screen, -} from '@folio/jest-config-stripes/testing-library/react'; - -import { - Modal, - MultiColumnList, - Pane, - Paneset, -} from '@folio/stripes/components'; - -import { - itemStatuses, - requestableItemStatuses, -} from '../../../constants'; -import { Loading } from '../../../components'; -import ItemsDialog, { - COLUMN_NAMES, - COLUMN_WIDTHS, - COLUMN_MAP, - formatter, - MAX_HEIGHT, -} from './ItemsDialog'; - -jest.mock('../../../components', () => ({ - Loading: jest.fn((props) => ( -
- )), -})); - -const testIds = { - loading: 'loading', -}; -const labelIds = { - selectItem: 'ui-requests.items.selectItem', - instanceItems: 'ui-requests.items.instanceItems', - resultCount: 'ui-requests.resultCount', - instanceItemsNotFound: 'ui-requests.items.instanceItems.notFound', -}; - -describe('ItemsDialog', () => { - const onClose = jest.fn(); - const onRowClick = jest.fn(); - const testTitle = 'testTitle'; - const testInstanceId = 'testInstanceId'; - const testMutator = { - holdings: { - GET: jest.fn(() => (new Promise((resolve) => { - setTimeout(() => { - resolve( - [{ - id: '1', - }, { - id: '2', - }] - ); - }); - }))), - reset: jest.fn(), - }, - items: { - GET: jest.fn(() => (new Promise((resolve) => { - setTimeout(() => { - resolve( - [{ - id: '1', - status: { - name: itemStatuses.IN_PROCESS, - }, - }, { - id: '2', - status: { - name: itemStatuses.AVAILABLE, - }, - }, { - id: '3', - status: { - name: itemStatuses.IN_TRANSIT, - }, - }] - ); - }); - }))), - reset: jest.fn(), - }, - requests: { - GET: jest.fn(() => (new Promise((resolve) => { - setTimeout(() => { - resolve( - [{ - id: '1', - itemId: '1', - }, { - id: '2', - itemId: '2', - }, { - id: '4', - itemId: '1', - }] - ); - }); - }))), - reset: jest.fn(), - }, - }; - const defaultTestProps = { - open: false, - onClose, - onRowClick, - instanceId: testInstanceId, - title: testTitle, - mutator: testMutator, - }; - - afterEach(() => { - Modal.mockClear(); - MultiColumnList.mockClear(); - Pane.mockClear(); - Paneset.mockClear(); - Loading.mockClear(); - onClose.mockClear(); - testMutator.holdings.GET.mockClear(); - testMutator.holdings.reset.mockClear(); - testMutator.items.GET.mockClear(); - testMutator.items.reset.mockClear(); - testMutator.requests.GET.mockClear(); - testMutator.requests.reset.mockClear(); - }); - - describe('with default props', () => { - beforeEach(() => { - render(); - }); - - it('should render Modal', () => { - expect(Modal).toHaveBeenCalledWith( - expect.objectContaining({ - 'data-test-move-request-modal': true, - label: labelIds.selectItem, - open: false, - onClose, - dismissible: true, - }), {} - ); - }); - - it('should render Paneset', () => { - expect(Paneset).toHaveBeenLastCalledWith( - expect.objectContaining({ - id: 'itemsDialog', - isRoot: true, - static: true, - }), {} - ); - }); - - it('should render Pane', () => { - expect(Pane).toHaveBeenLastCalledWith( - expect.objectContaining({ - paneTitle: labelIds.instanceItems, - paneSub: labelIds.resultCount, - defaultWidth: 'fill', - }), {} - ); - }); - - it('should not render Loading', () => { - expect(Loading).not.toHaveBeenCalled(); - }); - - it('should render MultiColumnList', () => { - expect(MultiColumnList).toHaveBeenLastCalledWith( - expect.objectContaining({ - id: 'instance-items-list', - interactive: true, - contentData: [], - visibleColumns: COLUMN_NAMES, - columnMapping: COLUMN_MAP, - columnWidths: COLUMN_WIDTHS, - formatter, - maxHeight: MAX_HEIGHT, - isEmptyMessage: labelIds.instanceItemsNotFound, - onRowClick, - }), {} - ); - }); - }); - - describe('when open prop is true', () => { - beforeEach(() => { - render( - - ); - }); - - it('should render Modal', () => { - expect(Modal).toHaveBeenNthCalledWith( - 1, - expect.objectContaining({ - 'data-test-move-request-modal': true, - label: labelIds.selectItem, - open: true, - onClose, - dismissible: true, - }), {} - ); - }); - - it('should render Loading when data is being loaded', () => { - const loading = screen.queryByTestId(testIds.loading); - - expect(loading).toBeInTheDocument(); - }); - - describe('when data is loaded', () => { - beforeEach(async () => { - await new Promise((resolve) => setTimeout(resolve, 500)); - }); - - it('should hide Loading when data is loaded', () => { - const loading = screen.queryByTestId(testIds.loading); - - expect(loading).not.toBeInTheDocument(); - }); - - it('should render MultiColumnList when data is loaded', () => { - expect(MultiColumnList).toHaveBeenLastCalledWith( - { - id: 'instance-items-list', - interactive: true, - contentData: [{ - id: '2', - status: { - name: itemStatuses.AVAILABLE, - }, - requestQueue: 1, - }, { - id: '1', - status: { - name: itemStatuses.IN_PROCESS, - }, - requestQueue: 2, - }, { - id: '3', - status: { - name: itemStatuses.IN_TRANSIT, - }, - requestQueue: 0, - }], - visibleColumns: COLUMN_NAMES, - columnMapping: COLUMN_MAP, - columnWidths: COLUMN_WIDTHS, - formatter, - maxHeight: MAX_HEIGHT, - isEmptyMessage: labelIds.instanceItemsNotFound, - onRowClick, - }, {} - ); - }); - - describe('when "items" response contains non-requestable items', () => { - const getProcessedItem = (status) => ({ - id: status, - requestQueue: 0, - status: { - name: status, - }, - }); - const allItemStatuses = Object.values(itemStatuses); - const newMutator = { - ...testMutator, - items: { - GET: jest.fn(() => (new Promise((resolve) => { - setTimeout(() => { - resolve( - allItemStatuses.map(status => ({ - id: status, - status: { - name: status, - }, - })), - ); - }); - }))), - reset: jest.fn(), - }, - requests: { - GET: jest.fn(() => (new Promise((resolve) => { - setTimeout(() => { - resolve([]); - }); - }))), - reset: jest.fn(), - }, - }; - - describe('within "move request" action', () => { - beforeEach(async () => { - render( - - ); - - await new Promise((resolve) => setTimeout(resolve, 500)); - }); - - it('should show only items with requestable statuses', () => { - const requestableItems = requestableItemStatuses.map(getProcessedItem); - - expect(MultiColumnList).toHaveBeenLastCalledWith(expect.objectContaining({ - contentData: expect.arrayContaining(requestableItems), - }), {}); - }); - - it('should not show items with non-requestable statuses', () => { - const nonRequestableItems = allItemStatuses.map(status => { - if (requestableItemStatuses.includes(status)) { - return null; - } - - return getProcessedItem(status); - }); - - expect(MultiColumnList).toHaveBeenLastCalledWith(expect.objectContaining({ - contentData: expect.not.arrayContaining(nonRequestableItems), - }), {}); - }); - }); - - describe('within switching from instance-level to item-level request', () => { - beforeEach(async () => { - render( - - ); - - await new Promise((resolve) => setTimeout(resolve, 500)); - }); - - it('should show items with all possible statuses', () => { - const expectedResult = allItemStatuses.map(getProcessedItem); - - expect(MultiColumnList).toHaveBeenLastCalledWith(expect.objectContaining({ - contentData: expect.arrayContaining(expectedResult), - }), {}); - }); - }); - }); - }); - }); - - [ - true, - false, - ].forEach((isLoading) => { - describe(`when isLoading is ${isLoading}`, () => { - beforeEach(() => { - render( - - ); - }); - - if (isLoading) { - it('should render Loading', () => { - expect(Loading).toHaveBeenCalledTimes(1); - }); - - it('should not render MultiColumnList', () => { - expect(MultiColumnList).toHaveBeenCalledTimes(0); - }); - } else { - it('should not render Loading', () => { - expect(Loading).toHaveBeenCalledTimes(0); - }); - - it('should render MultiColumnList', () => { - expect(MultiColumnList).toHaveBeenLastCalledWith( - { - id: 'instance-items-list', - interactive: true, - contentData: [], - visibleColumns: COLUMN_NAMES, - columnMapping: COLUMN_MAP, - columnWidths: COLUMN_WIDTHS, - formatter, - maxHeight: MAX_HEIGHT, - isEmptyMessage: labelIds.instanceItemsNotFound, - onRowClick, - }, {} - ); - }); - } - }); - }); - - describe('formatter', () => { - const item = { - status: { - name: 'Aged to lost', - }, - effectiveLocation: { - name: 'effective location name', - }, - materialType: { - name: 'material type name', - }, - temporaryLoanType: { - name: 'temporary loan type name', - }, - permanentLoanType: { - name: 'permanent loan type name', - }, - }; - - describe('itemStatus', () => { - it('should return formatted message', () => { - expect(formatter.itemStatus(item).props.id).toEqual('ui-requests.item.status.agedToLost'); - }); - }); - - describe('location', () => { - it('should return effective location name', () => { - expect(formatter.location(item)).toEqual('effective location name'); - }); - - it('should return default value for effective location name', () => { - expect(formatter.location({ - ...item, - effectiveLocation: {}, - })).toEqual(''); - }); - }); - - describe('materialType', () => { - it('should return material type', () => { - expect(formatter.materialType(item)).toEqual('material type name'); - }); - }); - - describe('loanType', () => { - describe('with temporaryLoanType', () => { - it('should return temporary loan type name', () => { - expect(formatter.loanType(item)).toEqual('temporary loan type name'); - }); - - it('should return default value for temporary loan type name', () => { - expect(formatter.loanType({ - ...item, - temporaryLoanType: { - other: '', - }, - })).toEqual(''); - }); - }); - - describe('without temporaryLoanType', () => { - it('should return permanent loan type name', () => { - expect(formatter.loanType({ - ...item, - temporaryLoanType: false, - })).toEqual('permanent loan type name'); - }); - - it('should return default value for permanent loan type name', () => { - expect(formatter.loanType({ - ...item, - temporaryLoanType: false, - permanentLoanType: { - other: '', - }, - })).toEqual(''); - }); - }); - }); - }); -}); diff --git a/src/deprecated/components/MoveRequestManager/MoveRequestManager.js b/src/deprecated/components/MoveRequestManager/MoveRequestManager.js deleted file mode 100644 index a8bb5205..00000000 --- a/src/deprecated/components/MoveRequestManager/MoveRequestManager.js +++ /dev/null @@ -1,257 +0,0 @@ -import { - get, - includes, -} from 'lodash'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { FormattedMessage } from 'react-intl'; - -import { - stripesConnect, - stripesShape, -} from '@folio/stripes/core'; -import { - getHeaderWithCredentials, -} from '@folio/stripes/util'; - -import ItemsDialog from '../ItemsDialog/ItemsDialog'; -import ChooseRequestTypeDialog from '../../../ChooseRequestTypeDialog'; -import ErrorModal from '../../../components/ErrorModal'; -import { getRequestTypeOptions } from '../../../utils'; -import { REQUEST_OPERATIONS } from '../../../constants'; - -class MoveRequestManager extends React.Component { - static propTypes = { - onCancelMove: PropTypes.func, - onMove: PropTypes.func, - request: PropTypes.object.isRequired, - mutator: PropTypes.shape({ - move: PropTypes.shape({ - POST: PropTypes.func.isRequired, - }), - moveRequest: PropTypes.shape({ - POST: PropTypes.func.isRequired, - }), - }).isRequired, - stripes: stripesShape.isRequired, - }; - - static manifest = { - moveRequest: { - type: 'okapi', - POST: { - path: 'circulation/requests/!{request.id}/move', - }, - fetch: false, - throwErrors: false, - }, - }; - - constructor(props) { - super(props); - this.state = { - moveRequest: true, - moveInProgress: false, - requestTypes: [], - isRequestTypesLoading: false, - }; - - this.steps = [ - { - validate: this.shouldChooseRequestTypeDialogBeShown, - exec: () => this.setState({ - chooseRequestType: true, - moveRequest: false, - }), - }, - ]; - } - - execSteps = (start) => { - for (let i = start; i < this.steps.length; i++) { - const step = this.steps[i]; - if (step.validate()) { - return step.exec(); - } - } - - return this.moveRequest(); - } - - shouldChooseRequestTypeDialogBeShown = () => { - const { requestTypes } = this.state; - const { - request: { - requestType, - }, - } = this.props; - - return !includes(requestTypes, requestType); - } - - confirmChoosingRequestType = (selectedRequestType) => { - this.setState({ - selectedRequestType, - }, () => this.execSteps(1)); - } - - moveRequest = async () => { - const { - selectedItem, - selectedRequestType, - } = this.state; - const { - mutator: { - moveRequest: { POST }, - }, - request, - } = this.props; - const requestType = selectedRequestType || request.requestType; - const destinationItemId = selectedItem.id; - const data = { - destinationItemId, - requestType, - }; - - this.setState({ moveInProgress: true }); - - try { - const movedRequest = await POST(data); - this.props.onMove(movedRequest); - - this.setState({ - chooseRequestType: false, - }); - } catch (resp) { - this.processError(resp); - } finally { - this.setState({ moveInProgress: false }); - } - } - - processError(resp) { - const contentType = resp.headers.get('Content-Type') || ''; - if (contentType.startsWith('application/json')) { - return resp.json().then(error => this.handleError(get(error, 'errors[0].message'))); - } else { - return resp.text().then(error => this.handleError(error)); - } - } - - handleError(errorMessage) { - this.setState({ - chooseRequestType: false, - errorMessage, - }); - } - - onItemSelected = (selectedItem) => { - const { - request, - stripes, - } = this.props; - const httpHeadersOptions = { - ...getHeaderWithCredentials({ - tenant: stripes.okapi.tenant, - token: stripes.store.getState().okapi.token, - }) - }; - const url = `${stripes.okapi.url}/circulation/requests/allowed-service-points?requestId=${request.id}&itemId=${selectedItem.id}&operation=${REQUEST_OPERATIONS.MOVE}`; - - this.setState({ - isRequestTypesLoading: true, - requestTypes: [], - }); - - fetch(url, httpHeadersOptions) - .then(res => { - if (res.ok) { - return res.json(); - } - - return Promise.reject(res); - }) - .then((res) => { - this.setState({ - requestTypes: Object.keys(res), - selectedItem, - }, () => this.execSteps(0)); - }) - .catch(() => { - this.execSteps(0); - }) - .finally(() => { - this.setState({ - isRequestTypesLoading: false, - }); - }); - } - - cancelMoveRequest = () => { - this.setState({ - moveRequest: true, - chooseRequestType: false, - selectedRequestType: '', - }); - } - - closeErrorMessage = () => { - const { requestTypes } = this.state; - const state = { errorMessage: null }; - - if (requestTypes && requestTypes.length > 1) { - state.chooseRequestType = true; - } else { - state.moveRequest = true; - } - - this.setState(state); - } - - render() { - const { - onCancelMove, - request, - } = this.props; - const { - chooseRequestType, - errorMessage, - moveRequest, - moveInProgress, - requestTypes, - isRequestTypesLoading, - } = this.state; - const isLoading = moveInProgress || isRequestTypesLoading; - - return ( - <> - this.onItemSelected(item)} - /> - {chooseRequestType && - } - {errorMessage && - } - errorMessage={errorMessage} - /> } - - ); - } -} - -export default stripesConnect(MoveRequestManager); diff --git a/src/deprecated/components/MoveRequestManager/MoveRequestManager.test.js b/src/deprecated/components/MoveRequestManager/MoveRequestManager.test.js deleted file mode 100644 index 1a4f0b17..00000000 --- a/src/deprecated/components/MoveRequestManager/MoveRequestManager.test.js +++ /dev/null @@ -1,404 +0,0 @@ -import { - render, - screen, - fireEvent, - cleanup, - waitFor, -} from '@folio/jest-config-stripes/testing-library/react'; -import userEvent from '@folio/jest-config-stripes/testing-library/user-event'; - -import MoveRequestManager from './MoveRequestManager'; -import ItemsDialog from '../ItemsDialog/ItemsDialog'; -import ChooseRequestTypeDialog from '../../../ChooseRequestTypeDialog'; -import ErrorModal from '../../../components/ErrorModal'; -import { - REQUEST_OPERATIONS, - requestTypeOptionMap, - requestTypesMap, -} from '../../../constants'; - -const labelIds = { - requestNotAllowed: 'ui-requests.requestNotAllowed', -}; -const testIds = { - rowButton: 'rowButton', - confirmButton: 'confirmButton', - cancelButton: 'cancelButton', - chooseRequestTypeDialog: 'chooseRequestTypeDialog', - errorModal: 'errorModal', - closeErrorModalButton: 'closeErrorModalButton', -}; -const movedRequest = {}; -const basicProps = { - onCancelMove: jest.fn(), - onMove: jest.fn(), - request: { - requestType: requestTypesMap.PAGE, - instanceId: 'instanceId', - itemId: 'itemId', - instance: { - title: 'instanceTitle', - }, - id: 'requestId', - }, - mutator: { - move: { - POST: jest.fn(), - }, - moveRequest: { - POST: jest.fn(async () => movedRequest), - }, - }, - stripes: { - okapi: { - url: 'okapiUrl', - tenant: 'okapiTenant', - }, - store: { - getState: () => ({ - okapi: { - token: 'okapiToken', - }, - }), - }, - }, -}; -const selectedItem = { - id: 'selectedItemId', - status: { - name: 'Checked out', - }, -}; - -jest.mock('../ItemsDialog/ItemsDialog', () => jest.fn(({ - children, - onRowClick, -}) => { - return ( -
- - {children} -
- ); -})); -jest.mock('../../../ChooseRequestTypeDialog', () => jest.fn(({ - onConfirm, - onCancel -}) => ( -
- - -
-))); -jest.mock('../../../components/ErrorModal', () => jest.fn(({ - label, - onClose, -}) => ( -
-

{label}

- -
-))); - -describe('MoveRequestManager', () => { - beforeEach(() => { - global.fetch = jest.fn(() => Promise.resolve({ - json: () => ({ - [requestTypesMap.HOLD]: ['id'], - }) - })); - }); - - afterEach(() => { - jest.clearAllMocks(); - jest.restoreAllMocks(); - cleanup(); - }); - - describe('Initial rendering', () => { - beforeEach(() => { - render( - - ); - }); - - it('should trigger "ItemsDialog" with correct props', () => { - const expectedProps = { - open: true, - instanceId: basicProps.request.instanceId, - title: basicProps.request.instance.title, - isLoading: false, - onClose: basicProps.onCancelMove, - skippedItemId: basicProps.request.itemId, - onRowClick: expect.any(Function), - }; - - expect(ItemsDialog).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - - it('should not trigger "ChooseRequestTypeDialog"', () => { - expect(ChooseRequestTypeDialog).not.toHaveBeenCalled(); - }); - - it('should not trigger "ErrorModal"', () => { - expect(ErrorModal).not.toHaveBeenCalled(); - }); - }); - - describe('Request type dialog', () => { - beforeEach(() => { - global.fetch = jest.fn(() => Promise.resolve({ - ok: true, - json: () => ({ - [requestTypesMap.HOLD]: ['id'], - }) - })); - - render( - - ); - - const rowButton = screen.getByTestId(testIds.rowButton); - - fireEvent.click(rowButton); - }); - - it('should trigger fetch with correct argument', () => { - const expectedUrl = `${basicProps.stripes.okapi.url}/circulation/requests/allowed-service-points?requestId=${basicProps.request.id}&itemId=${selectedItem.id}&operation=${REQUEST_OPERATIONS.MOVE}`; - - expect(global.fetch).toHaveBeenCalledWith(expectedUrl, {}); - }); - - it('should trigger "ChooseRequestTypeDialog" with correct props', async () => { - const expectedProps = { - open: true, - 'data-test-choose-request-type-modal': true, - onConfirm: expect.any(Function), - onCancel: expect.any(Function), - isLoading: false, - requestTypes: [ - { - id: requestTypeOptionMap[requestTypesMap.HOLD], - value: requestTypesMap.HOLD, - } - ], - }; - - await waitFor(() => expect(ChooseRequestTypeDialog).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {})); - }); - - it('should hide request type dialog after clicking on "Cancel" button', async () => { - await waitFor(() => { - const cancelButton = screen.getByTestId(testIds.cancelButton); - const chooseRequestTypeDialog = screen.getByTestId(testIds.chooseRequestTypeDialog); - - fireEvent.click(cancelButton); - - expect(chooseRequestTypeDialog).not.toBeInTheDocument(); - }); - }); - - it('should trigger "moveRequest.POST" after clicking on "Confirm" button', async () => { - await waitFor(() => { - const confirmButton = screen.getByTestId(testIds.confirmButton); - - fireEvent.click(confirmButton); - - expect(basicProps.mutator.moveRequest.POST).toHaveBeenCalled(); - }); - }); - - it('should trigger "onMove" with correct argument after clicking on "Confirm" button', async () => { - await waitFor(async () => { - const confirmButton = screen.getByTestId(testIds.confirmButton); - - fireEvent.click(confirmButton); - - await waitFor(() => { - expect(basicProps.onMove).toHaveBeenCalledWith(movedRequest); - }); - }); - }); - }); - - describe('Error modal', () => { - describe('When response content type is "application/json"', () => { - const error = { - errors: [ - { - message: 'Error message', - } - ] - }; - const get = jest.fn(() => 'application/json'); - const json = jest.fn(() => new Promise(res => res(error))); - - beforeEach(async () => { - global.fetch = jest.fn(() => Promise.resolve({ - ok: true, - json: () => ({ - [requestTypesMap.HOLD]: ['holdId'], - [requestTypesMap.RECALL]: ['recallId'], - }) - })); - - const props = { - ...basicProps, - mutator: { - ...basicProps.mutator, - moveRequest: { - POST: () => { - const errorToThrow = new Error('message'); - - errorToThrow.headers = { - get, - }; - errorToThrow.json = json; - - throw errorToThrow; - }, - }, - }, - }; - - render( - - ); - - const rowButton = screen.getByTestId(testIds.rowButton); - - await userEvent.click(rowButton); - - const confirmButton = screen.getByTestId(testIds.confirmButton); - - await userEvent.click(confirmButton); - }); - - it('should trigger "ErrorModal" with correct props', async () => { - const expectedProps = { - onClose: expect.any(Function), - errorMessage: error.errors[0].message, - }; - - await waitFor(() => expect(ErrorModal).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {})); - }); - - it('should render "ErrorModal" label', async () => { - const requestNotAllowedLabel = screen.getByText(labelIds.requestNotAllowed); - - await waitFor(() => expect(requestNotAllowedLabel).toBeInTheDocument()); - }); - - it('should not render "ChooseRequestTypeDialog"', async () => { - const chooseRequestTypeDialog = screen.queryByTestId(testIds.chooseRequestTypeDialog); - - await waitFor(() => expect(chooseRequestTypeDialog).not.toBeInTheDocument()); - }); - - it('should hide "ErrorModal"', async () => { - const errorModal = screen.getByTestId(testIds.errorModal); - const closeErrorModalButton = screen.getByTestId(testIds.closeErrorModalButton); - - fireEvent.click(closeErrorModalButton); - - await waitFor(() => expect(errorModal).not.toBeInTheDocument()); - }); - - it('should render "ChooseRequestTypeDialog" after closing error modal', async () => { - const closeErrorModalButton = screen.getByTestId(testIds.closeErrorModalButton); - - fireEvent.click(closeErrorModalButton); - - const chooseRequestTypeDialog = screen.queryByTestId(testIds.chooseRequestTypeDialog); - - await waitFor(() => expect(chooseRequestTypeDialog).toBeInTheDocument()); - }); - }); - - describe('When response content type is not "application/json"', () => { - const error = 'Test error'; - const get = jest.fn(() => ''); - const text = jest.fn(() => new Promise(res => res(error))); - - beforeEach(async () => { - const props = { - ...basicProps, - mutator: { - ...basicProps.mutator, - moveRequest: { - POST: () => { - const errorToThrow = new Error('message'); - - errorToThrow.text = text; - errorToThrow.headers = { - get, - }; - - throw errorToThrow; - }, - }, - }, - }; - - global.fetch = jest.fn(() => Promise.resolve({ - ok: true, - json: () => ({ - [requestTypesMap.HOLD]: ['holdId'], - [requestTypesMap.RECALL]: ['recallId'], - }) - })); - - render( - - ); - - const rowButton = screen.getByTestId(testIds.rowButton); - - await waitFor(() => { - fireEvent.click(rowButton); - - const confirmButton = screen.getByTestId(testIds.confirmButton); - - fireEvent.click(confirmButton); - }); - }); - - it('should trigger "ErrorModal" with correct props', async () => { - const expectedProps = { - onClose: expect.any(Function), - errorMessage: error, - }; - - await waitFor(() => expect(ErrorModal).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {})); - }); - }); - }); -}); diff --git a/src/deprecated/components/RequestForm/RequestForm.css b/src/deprecated/components/RequestForm/RequestForm.css deleted file mode 100644 index 9c5c8b4a..00000000 --- a/src/deprecated/components/RequestForm/RequestForm.css +++ /dev/null @@ -1,17 +0,0 @@ -.requestForm { - height: 100%; - width: 100%; - overflow: auto; -} - -.footerContent { - max-width: 50em; - display: flex; - justify-content: space-between; - width: 100%; -} - -.tlrCheckbox { - margin-bottom: 0.5em; - user-select: none; -} diff --git a/src/deprecated/components/RequestForm/RequestForm.js b/src/deprecated/components/RequestForm/RequestForm.js deleted file mode 100644 index 8094ebfc..00000000 --- a/src/deprecated/components/RequestForm/RequestForm.js +++ /dev/null @@ -1,1414 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { - Field, -} from 'react-final-form'; -import { - FormattedMessage, -} from 'react-intl'; -import { parse } from 'query-string'; - -import { - sortBy, - find, - get, - isEqual, - isEmpty, - keyBy, - defer, - pick, - isBoolean, - isNil, -} from 'lodash'; - -import { - Accordion, - AccordionSet, - Button, - Col, - Pane, - PaneFooter, - PaneHeaderIconButton, - PaneMenu, - Paneset, - Row, - Checkbox, - AccordionStatus, -} from '@folio/stripes/components'; -import stripesFinalForm from '@folio/stripes/final-form'; - -import RequestFormShortcutsWrapper from '../../../components/RequestFormShortcutsWrapper'; -import CancelRequestDialog from '../../../CancelRequestDialog'; -import PatronBlockModal from '../../../PatronBlockModal'; -import { - ErrorModal, - RequestInformation, - RequesterInformation, - FulfilmentPreference, -} from '../../../components'; -import ItemInformation from '../ItemInformation/ItemInformation'; -import InstanceInformation from '../InstanceInformation/InstanceInformation'; -import ItemsDialog from '../ItemsDialog/ItemsDialog'; -import { - iconTypes, - fulfillmentTypeMap, - createModes, - REQUEST_LEVEL_TYPES, - RESOURCE_KEYS, - REQUEST_FORM_FIELD_NAMES, - DEFAULT_REQUEST_TYPE_VALUE, - requestTypeOptionMap, - REQUEST_LAYERS, - REQUEST_OPERATIONS, -} from '../../../constants'; -import { RESOURCE_TYPES } from '../../constants'; -import { - handleKeyCommand, - toUserAddress, - getPatronGroup, - isDelivery, - parseErrorMessage, - getFulfillmentTypeOptions, - getDefaultRequestPreferences, - getFulfillmentPreference, - isDeliverySelected, - getSelectedAddressTypeId, - getProxy, - isSubmittingButtonDisabled, - isFormEditing, - resetFieldState, -} from '../../../utils'; -import { - getTlrSettings, - getRequester, -} from '../../utils'; - -import css from './RequestForm.css'; - -export const ID_TYPE_MAP = { - ITEM_ID: 'itemId', - INSTANCE_ID: 'instanceId', -}; -export const getResourceTypeId = (isTitleLevelRequest) => (isTitleLevelRequest ? ID_TYPE_MAP.INSTANCE_ID : ID_TYPE_MAP.ITEM_ID); -export const isTLR = (createTitleLevelRequest, requestLevel) => (createTitleLevelRequest || requestLevel === REQUEST_LEVEL_TYPES.TITLE); -export const getRequestInformation = (values, selectedInstance, selectedItem, request) => { - const isTitleLevelRequest = isTLR(values.createTitleLevelRequest, request?.requestLevel); - const selectedResource = isTitleLevelRequest ? selectedInstance : selectedItem; - - return { - isTitleLevelRequest, - selectedResource, - }; -}; - -class RequestForm extends React.Component { - static propTypes = { - stripes: PropTypes.shape({ - connect: PropTypes.func.isRequired, - store: PropTypes.shape({ - getState: PropTypes.func.isRequired, - }).isRequired, - }).isRequired, - errorMessage: PropTypes.string, - handleSubmit: PropTypes.func.isRequired, - findResource: PropTypes.func.isRequired, - request: PropTypes.object, - metadataDisplay: PropTypes.func, - initialValues: PropTypes.object, - location: PropTypes.shape({ - pathname: PropTypes.string.isRequired, - search: PropTypes.string, - }).isRequired, - onCancel: PropTypes.func.isRequired, - onCancelRequest: PropTypes.func, - pristine: PropTypes.bool, - resources: PropTypes.shape({ - query: PropTypes.object, - }), - submitting: PropTypes.bool, - toggleModal: PropTypes.func, - optionLists: PropTypes.shape({ - addressTypes: PropTypes.arrayOf(PropTypes.object), - fulfillmentTypes: PropTypes.arrayOf(PropTypes.object), - servicePoints: PropTypes.arrayOf(PropTypes.object), - }), - patronGroups: PropTypes.arrayOf(PropTypes.object), - parentResources: PropTypes.object, - history: PropTypes.shape({ - push: PropTypes.func, - }), - intl: PropTypes.object, - onChangePatron: PropTypes.func, - query: PropTypes.object, - selectedItem: PropTypes.object, - selectedInstance: PropTypes.object, - selectedUser: PropTypes.object, - values: PropTypes.object.isRequired, - form: PropTypes.object.isRequired, - blocked: PropTypes.bool.isRequired, - instanceId: PropTypes.string.isRequired, - isPatronBlocksOverridden: PropTypes.bool.isRequired, - onSubmit: PropTypes.func, - parentMutator: PropTypes.shape({ - proxy: PropTypes.shape({ - reset: PropTypes.func.isRequired, - GET: PropTypes.func.isRequired, - }).isRequired, - }).isRequired, - isTlrEnabledOnEditPage: PropTypes.bool, - onGetAutomatedPatronBlocks: PropTypes.func.isRequired, - onGetPatronManualBlocks: PropTypes.func.isRequired, - onSetSelectedItem: PropTypes.func.isRequired, - onSetSelectedUser: PropTypes.func.isRequired, - onSetSelectedInstance: PropTypes.func.isRequired, - onSetBlocked: PropTypes.func.isRequired, - onSetIsPatronBlocksOverridden: PropTypes.func.isRequired, - onSetInstanceId: PropTypes.func.isRequired, - }; - - static defaultProps = { - request: null, - metadataDisplay: () => { }, - optionLists: {}, - pristine: true, - submitting: false, - isTlrEnabledOnEditPage: false, - }; - - constructor(props) { - super(props); - - const { - request, - initialValues, - } = props; - const { - loan, - } = (request || {}); - - const { titleLevelRequestsFeatureEnabled } = this.getTlrSettings(); - - this.state = { - proxy: null, - selectedLoan: loan, - ...getDefaultRequestPreferences(request, initialValues), - isAwaitingForProxySelection: false, - titleLevelRequestsFeatureEnabled, - isItemOrInstanceLoading: false, - isItemsDialogOpen: false, - isItemIdRequest: this.isItemIdProvided(), - requestTypes: {}, - isRequestTypesReceived: false, - isRequestTypeLoading: false, - isRequestTypesForDuplicate: false, - isRequestTypesForEditing: false, - }; - - this.connectedCancelRequestDialog = props.stripes.connect(CancelRequestDialog); - this.onChangeAddress = this.onChangeAddress.bind(this); - this.onSelectProxy = this.onSelectProxy.bind(this); - this.onClose = this.onClose.bind(this); - this.accordionStatusRef = React.createRef(); - } - - componentDidMount() { - const { - query: { - userId, - }, - } = this.props; - - if (this.props.query.userBarcode) { - this.findUser(RESOURCE_KEYS.barcode, this.props.query.userBarcode); - } else if (userId) { - this.findUser(RESOURCE_KEYS.id, userId); - } - - if (this.props.query.itemBarcode) { - this.findItem(RESOURCE_KEYS.barcode, this.props.query.itemBarcode); - } - - if (this.props.query.itemId) { - this.findItem(RESOURCE_KEYS.id, this.props.query.itemId); - } - - if (this.props.query.instanceId && !this.props.query.itemBarcode && !this.props.query.itemId) { - this.findInstance(this.props.query.instanceId); - } - - if (isFormEditing(this.props.request)) { - this.findRequestPreferences(this.props.initialValues.requesterId); - } - - this.setTlrCheckboxInitialState(); - } - - componentDidUpdate(prevProps) { - const { - isRequestTypesForDuplicate, - isRequestTypesForEditing, - } = this.state; - const { - initialValues, - request, - values, - parentResources, - query, - selectedItem, - selectedUser, - selectedInstance, - onGetAutomatedPatronBlocks, - onGetPatronManualBlocks, - onSetBlocked, - onSetSelectedItem, - onSetSelectedUser, - } = this.props; - - const { - initialValues: prevInitialValues, - request: prevRequest, - parentResources: prevParentResources, - query: prevQuery, - } = prevProps; - - const prevBlocks = onGetPatronManualBlocks(prevParentResources); - const blocks = onGetPatronManualBlocks(parentResources); - const prevAutomatedPatronBlocks = onGetAutomatedPatronBlocks(prevParentResources); - const automatedPatronBlocks = onGetAutomatedPatronBlocks(parentResources); - const { item } = initialValues; - - if ( - (initialValues && - initialValues.fulfillmentPreference && - prevInitialValues && - !prevInitialValues.fulfillmentPreference) || - !isEqual(request, prevRequest) - ) { - onSetSelectedItem(request.item); - onSetSelectedUser(request.requester); - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ - selectedAddressTypeId: initialValues.deliveryAddressTypeId, - deliverySelected: isDelivery(initialValues), - selectedLoan: request.loan, - }); - } - - // When in duplicate mode there are cases when selectedItem from state - // is missing or not set. In this case just set it to initial item. - if (query?.mode === createModes.DUPLICATE && - item && !selectedItem) { - onSetSelectedItem(item); - this.triggerItemBarcodeValidation(); - } - - if (query?.mode === createModes.DUPLICATE && - (selectedItem?.id || selectedInstance?.id) && - selectedUser?.id && - !isRequestTypesForDuplicate - ) { - this.setState({ - isRequestTypesForDuplicate: true, - }); - this.getAvailableRequestTypes(selectedUser); - } - - if (query?.layer === REQUEST_LAYERS.EDIT && - !isRequestTypesForEditing - ) { - this.setState({ - isRequestTypesForEditing: true, - }); - - const isTitleLevelRequest = isTLR(values.createTitleLevelRequest, request.requestLevel); - const resourceTypeId = getResourceTypeId(isTitleLevelRequest); - const resourceId = isTitleLevelRequest ? request.instanceId : request.itemId; - - this.findRequestTypes(resourceId, request.requester.id || request.requesterId, resourceTypeId); - } - - if (prevQuery.userBarcode !== query.userBarcode) { - this.findUser(RESOURCE_KEYS.barcode, query.userBarcode); - } - - if (prevQuery.userId !== query.userId) { - this.findUser(RESOURCE_KEYS.id, query.userId); - } - - if (prevQuery.itemBarcode !== query.itemBarcode) { - this.findItem(RESOURCE_KEYS.barcode, query.itemBarcode); - } - - if (prevQuery.itemId !== query.itemId) { - this.findItem(RESOURCE_KEYS.id, query.itemId); - } - - if (prevQuery.instanceId !== query.instanceId) { - this.findInstance(query.instanceId); - this.setTlrCheckboxInitialState(); - } - - if (!isEqual(blocks, prevBlocks) && blocks.length > 0) { - const user = selectedUser || {}; - if (user.id === blocks[0].userId) { - onSetBlocked(true); - } - } - - if (!isEqual(prevAutomatedPatronBlocks, automatedPatronBlocks) && !isEmpty(automatedPatronBlocks)) { - if (selectedUser.id) { - onSetBlocked(true); - } - } - - if (prevParentResources?.configs?.records[0]?.value !== parentResources?.configs?.records[0]?.value) { - const { - titleLevelRequestsFeatureEnabled, - } = this.getTlrSettings(); - - // eslint-disable-next-line react/no-did-update-set-state - this.setState( - { - titleLevelRequestsFeatureEnabled, - }, - this.setTlrCheckboxInitialState(), - ); - } - } - - isItemIdProvided = () => { - const { - query, - location, - } = this.props; - const itemId = query?.itemId || parse(location.search)?.itemId; - - return Boolean(itemId); - } - - getTlrSettings() { - return getTlrSettings(this.props.parentResources?.configs?.records[0]?.value); - } - - setTlrCheckboxInitialState() { - const { - form, - } = this.props; - - if (this.state.titleLevelRequestsFeatureEnabled === false) { - form.change(REQUEST_FORM_FIELD_NAMES.CREATE_TLR, false); - return; - } - - if (this.props.query.itemId || this.props.query.itemBarcode) { - form.change(REQUEST_FORM_FIELD_NAMES.CREATE_TLR, false); - } else if (this.props.query.instanceId) { - form.change(REQUEST_FORM_FIELD_NAMES.CREATE_TLR, true); - } - } - - onClose() { - this.props.toggleModal(); - } - - changeDeliveryAddress = (deliverySelected, selectedAddressTypeId) => { - this.setState({ - deliverySelected, - selectedAddressTypeId, - }, () => { - this.updateRequestPreferencesFields(); - }); - } - - onChangeAddress(e) { - const { form } = this.props; - const selectedAddressTypeId = e.target.value; - - form.change(REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, selectedAddressTypeId); - this.setState({ - selectedAddressTypeId, - }); - } - - getAvailableRequestTypes = (user) => { - const { - selectedItem, - selectedInstance, - request, - values, - } = this.props; - const { - selectedResource, - isTitleLevelRequest, - } = getRequestInformation(values, selectedInstance, selectedItem, request); - - if (selectedResource?.id && user?.id) { - const resourceTypeId = getResourceTypeId(isTitleLevelRequest); - - this.findRequestTypes(selectedResource.id, user.id, resourceTypeId); - } - } - - // Executed when a user is selected from the proxy dialog, - // regardless of whether the selection is "self" or an actual proxy - onSelectProxy(proxy) { - const { - form, - selectedUser, - } = this.props; - - if (selectedUser.id === proxy.id) { - this.setState({ - proxy: selectedUser, - }); - } else { - this.setState({ - proxy, - requestTypes: {}, - isRequestTypesReceived: false, - }); - form.change(REQUEST_FORM_FIELD_NAMES.REQUESTER_ID, proxy.id); - form.change(REQUEST_FORM_FIELD_NAMES.PROXY_USER_ID, selectedUser.id); - this.findRequestPreferences(proxy.id); - this.getAvailableRequestTypes(proxy); - } - - this.setState({ isAwaitingForProxySelection: false }); - } - - async hasProxies(user) { - if (!user) { - this.setState({ isAwaitingForProxySelection: false }); - - return null; - } - - const { parentMutator: mutator } = this.props; - const query = `query=(proxyUserId==${user.id})`; - - mutator.proxy.reset(); - - const userProxies = await mutator.proxy.GET({ params: { query } }); - - if (userProxies.length) { - this.setState({ isAwaitingForProxySelection: true }); - } else { - this.setState({ isAwaitingForProxySelection: false }); - } - - return user; - } - - shouldSetBlocked = (blocks, selectedUser) => { - return blocks.length && blocks[0].userId === selectedUser.id; - } - - findUser = (fieldName, value, isValidation = false) => { - const { - form, - findResource, - parentResources, - onChangePatron, - onGetPatronManualBlocks, - onGetAutomatedPatronBlocks, - onSetIsPatronBlocksOverridden, - onSetSelectedUser, - onSetBlocked, - } = this.props; - - this.setState({ - isUserLoading: true, - }); - - if (isValidation) { - return findResource(RESOURCE_TYPES.USER, value, fieldName) - .then((result) => { - return result.totalRecords; - }) - .finally(() => { - this.setState({ isUserLoading: false }); - }); - } else { - this.setState({ - proxy: null, - requestTypes: {}, - isRequestTypesReceived: false, - }); - form.change(REQUEST_FORM_FIELD_NAMES.PICKUP_SERVICE_POINT_ID, undefined); - form.change(REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, undefined); - form.change(REQUEST_FORM_FIELD_NAMES.PROXY_USER_ID, undefined); - - return findResource(RESOURCE_TYPES.USER, value, fieldName) - .then((result) => { - this.setState({ isAwaitingForProxySelection: true }); - - if (result.totalRecords === 1) { - const blocks = onGetPatronManualBlocks(parentResources); - const automatedPatronBlocks = onGetAutomatedPatronBlocks(parentResources); - const isAutomatedPatronBlocksRequestInPendingState = parentResources.automatedPatronBlocks.isPending; - const selectedUser = result.users[0]; - onChangePatron(selectedUser); - form.change(REQUEST_FORM_FIELD_NAMES.REQUESTER_ID, selectedUser.id); - form.change(REQUEST_FORM_FIELD_NAMES.REQUESTER, selectedUser); - onSetSelectedUser(selectedUser); - - if (fieldName === RESOURCE_KEYS.id) { - this.triggerUserBarcodeValidation(); - } - - this.findRequestPreferences(selectedUser.id); - - if (this.shouldSetBlocked(blocks, selectedUser) || (!isEmpty(automatedPatronBlocks) && !isAutomatedPatronBlocksRequestInPendingState)) { - onSetBlocked(true); - onSetIsPatronBlocksOverridden(false); - } - - return selectedUser; - } - - return null; - }) - .then(user => { - this.getAvailableRequestTypes(user); - - return user; - }) - .then(user => this.hasProxies(user)) - .finally(() => { - this.setState({ isUserLoading: false }); - }); - } - } - - async findRequestPreferences(userId) { - const { - findResource, - form, - request, - initialValues, - } = this.props; - - try { - const { requestPreferences } = await findResource('requestPreferences', userId, 'userId'); - const preferences = requestPreferences[0]; - - const defaultPreferences = getDefaultRequestPreferences(request, initialValues); - const requestPreference = { - ...defaultPreferences, - ...pick(preferences, ['defaultDeliveryAddressTypeId', 'defaultServicePointId']), - requestPreferencesLoaded: true, - }; - - // when in edit mode (editing existing request) and defaultServicePointId is present (it was - // set during creation) just keep it instead of choosing the preferred one. - // https://issues.folio.org/browse/UIREQ-544 - if (isFormEditing(request) && defaultPreferences.defaultServicePointId) { - requestPreference.defaultServicePointId = defaultPreferences.defaultServicePointId; - } - - const deliveryIsPredefined = get(preferences, 'delivery'); - - if (isBoolean(deliveryIsPredefined)) { - requestPreference.hasDelivery = deliveryIsPredefined; - } - - const fulfillmentPreference = getFulfillmentPreference(preferences, initialValues); - const deliverySelected = isDeliverySelected(fulfillmentPreference); - - const selectedAddress = requestPreference.selectedAddressTypeId || requestPreference.defaultDeliveryAddressTypeId; - - const selectedAddressTypeId = getSelectedAddressTypeId(deliverySelected, selectedAddress); - - this.setState({ - ...requestPreference, - deliverySelected, - selectedAddressTypeId, - }, () => { - form.change(REQUEST_FORM_FIELD_NAMES.FULFILLMENT_PREFERENCE, fulfillmentPreference); - - this.updateRequestPreferencesFields(); - }); - } catch (e) { - this.setState({ - ...getDefaultRequestPreferences(request, initialValues), - deliverySelected: false, - }, () => { - form.change(REQUEST_FORM_FIELD_NAMES.FULFILLMENT_PREFERENCE, fulfillmentTypeMap.HOLD_SHELF); - }); - } - } - - updateRequestPreferencesFields = () => { - const { - defaultDeliveryAddressTypeId, - defaultServicePointId, - deliverySelected, - selectedAddressTypeId, - } = this.state; - const { - initialValues: { - requesterId, - }, - form, - selectedUser, - } = this.props; - - if (deliverySelected) { - const deliveryAddressTypeId = selectedAddressTypeId || defaultDeliveryAddressTypeId; - - form.change(REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, deliveryAddressTypeId); - form.change(REQUEST_FORM_FIELD_NAMES.PICKUP_SERVICE_POINT_ID, ''); - } else { - // Only change pickupServicePointId to defaultServicePointId - // if selected user has changed (by choosing a different user manually) - // or if the request form is not in a DUPLICATE mode. - // In DUPLICATE mode the pickupServicePointId from a duplicated request record will be used instead. - if (requesterId !== selectedUser?.id || this.props?.query?.mode !== createModes.DUPLICATE) { - form.change(REQUEST_FORM_FIELD_NAMES.PICKUP_SERVICE_POINT_ID, defaultServicePointId); - } - form.change(REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, ''); - } - } - - findRequestTypes = (resourceId, requesterId, resourceType) => { - const { - findResource, - form, - request, - } = this.props; - const isEditForm = isFormEditing(request); - let requestParams; - - if (isEditForm) { - requestParams = { - operation: REQUEST_OPERATIONS.REPLACE, - requestId: request.id, - }; - } else { - requestParams = { - operation: REQUEST_OPERATIONS.CREATE, - [resourceType]: resourceId, - requesterId, - }; - form.change(REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE, DEFAULT_REQUEST_TYPE_VALUE); - } - - this.setState({ - isRequestTypeLoading: true, - }); - - findResource(RESOURCE_TYPES.REQUEST_TYPES, requestParams) - .then(requestTypes => { - if (!isEmpty(requestTypes)) { - this.setState({ - requestTypes, - isRequestTypesReceived: true, - }, this.triggerRequestTypeValidation); - } else { - this.setState({ - isRequestTypesReceived: true, - }, this.triggerRequestTypeValidation); - } - }) - .finally(() => { - this.setState({ - isRequestTypeLoading: false, - }); - }); - } - - findItemRelatedResources(item) { - const { - findResource, - onSetInstanceId, - } = this.props; - if (!item) return null; - - return Promise.all( - [ - findResource('loan', item.id), - findResource('requestsForItem', item.id), - findResource(RESOURCE_TYPES.HOLDING, item.holdingsRecordId), - ], - ).then((results) => { - const selectedLoan = results[0]?.loans?.[0]; - const itemRequestCount = results[1]?.requests?.length; - const holdingsRecord = results[2]?.holdingsRecords?.[0]; - - onSetInstanceId(holdingsRecord?.instanceId); - this.setState({ - itemRequestCount, - selectedLoan, - }); - - return item; - }); - } - - setItemIdRequest = (key, isBarcodeRequired) => { - const { isItemIdRequest } = this.state; - - if (key === RESOURCE_KEYS.id && !isBarcodeRequired) { - this.setState({ - isItemIdRequest: true, - }); - } else if (key === RESOURCE_KEYS.barcode && isItemIdRequest) { - this.setState({ - isItemIdRequest: false, - }); - } - }; - - findItem = (key, value, isValidation = false, isBarcodeRequired = false) => { - const { - findResource, - form, - onSetSelectedItem, - selectedUser, - } = this.props; - const { proxy } = this.state; - - this.setState({ - isItemOrInstanceLoading: true, - }); - - if (isValidation) { - return findResource(RESOURCE_TYPES.ITEM, value, key) - .then((result) => { - return result.totalRecords; - }) - .finally(() => { - this.setState({ isItemOrInstanceLoading: false }); - }); - } else { - this.setState({ - requestTypes: {}, - isRequestTypesReceived: false, - }); - - return findResource(RESOURCE_TYPES.ITEM, value, key) - .then((result) => { - this.setItemIdRequest(key, isBarcodeRequired); - - if (!result || result.totalRecords === 0) { - this.setState({ - isItemOrInstanceLoading: false, - }); - - return null; - } - - const item = result.items[0]; - - form.change(REQUEST_FORM_FIELD_NAMES.ITEM_ID, item.id); - form.change(REQUEST_FORM_FIELD_NAMES.ITEM_BARCODE, item.barcode); - resetFieldState(form, REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE); - - // Setting state here is redundant with what follows, but it lets us - // display the matched item as quickly as possible, without waiting for - // the slow loan and request lookups - onSetSelectedItem(item); - this.setState({ - isItemOrInstanceLoading: false, - }); - - return item; - }) - .then(item => { - if (item && selectedUser?.id) { - const requester = getRequester(proxy, selectedUser); - this.findRequestTypes(item.id, requester.id, ID_TYPE_MAP.ITEM_ID); - } - - return item; - }) - .then(item => this.findItemRelatedResources(item)); - } - } - - findInstanceRelatedResources(instance) { - if (!instance) { - return null; - } - - const { findResource } = this.props; - - return findResource('requestsForInstance', instance.id) - .then((result) => { - const instanceRequestCount = result.requests.filter(r => r.requestLevel === REQUEST_LEVEL_TYPES.TITLE).length || 0; - - this.setState({ instanceRequestCount }); - - return instance; - }); - } - - findInstance = async (instanceId, holdingsRecordId, isValidation = false) => { - const { - findResource, - form, - onSetSelectedInstance, - selectedUser, - } = this.props; - const { proxy } = this.state; - - this.setState({ - isItemOrInstanceLoading: true, - }); - - const resultInstanceId = isNil(instanceId) - ? await findResource(RESOURCE_TYPES.HOLDING, holdingsRecordId).then((holding) => holding.holdingsRecords[0].instanceId) - : instanceId; - - if (isValidation) { - return findResource(RESOURCE_TYPES.INSTANCE, resultInstanceId) - .then((result) => { - return result.totalRecords; - }) - .finally(() => { - this.setState({ isItemOrInstanceLoading: false }); - }); - } else { - this.setState({ - requestTypes: {}, - isRequestTypesReceived: false, - }); - - return findResource(RESOURCE_TYPES.INSTANCE, resultInstanceId) - .then((result) => { - if (!result || result.totalRecords === 0) { - this.setState({ - isItemOrInstanceLoading: false, - }); - - return null; - } - - const instance = result.instances[0]; - - form.change(REQUEST_FORM_FIELD_NAMES.INSTANCE_ID, instance.id); - form.change(REQUEST_FORM_FIELD_NAMES.INSTANCE_HRID, instance.hrid); - resetFieldState(form, REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE); - - onSetSelectedInstance(instance); - this.setState({ - isItemOrInstanceLoading: false, - }); - - return instance; - }) - .then(instance => { - if (instance && selectedUser?.id) { - const requester = getRequester(proxy, selectedUser); - this.findRequestTypes(instance.id, requester.id, ID_TYPE_MAP.INSTANCE_ID); - } - - return instance; - }) - .then(instance => { - this.findInstanceRelatedResources(instance); - - return instance; - }); - } - } - - triggerItemBarcodeValidation = () => { - const { - form, - values, - } = this.props; - - form.change('keyOfItemBarcodeField', values.keyOfItemBarcodeField ? 0 : 1); - }; - - triggerUserBarcodeValidation = () => { - const { - form, - values, - } = this.props; - - form.change('keyOfUserBarcodeField', values.keyOfUserBarcodeField ? 0 : 1); - }; - - triggerInstanceIdValidation = () => { - const { - form, - values, - } = this.props; - - form.change('keyOfInstanceIdField', values.keyOfInstanceIdField ? 0 : 1); - }; - - triggerRequestTypeValidation = () => { - const { - form, - values, - } = this.props; - - form.change('keyOfRequestTypeField', values.keyOfRequestTypeField ? 0 : 1); - }; - - onCancelRequest = (cancellationInfo) => { - this.setState({ isCancellingRequest: false }); - this.props.onCancelRequest(cancellationInfo); - } - - onCloseBlockedModal = () => { - const { - onSetBlocked, - } = this.props; - - onSetBlocked(false); - } - - onViewUserPath(selectedUser, patronGroup) { - // reinitialize form (mark it as pristine) - this.props.form.reset(); - // wait for the form to be reinitialized - defer(() => { - this.setState({ isCancellingRequest: false }); - const viewUserPath = `/users/view/${(selectedUser || {}).id}?filters=pg.${patronGroup.group}`; - this.props.history.push(viewUserPath); - }); - } - - renderAddRequestFirstMenu = () => ( - - - {title => ( - - )} - - - ); - - overridePatronBlocks = () => { - const { - onSetIsPatronBlocksOverridden, - } = this.props; - - onSetIsPatronBlocksOverridden(true); - }; - - handleTlrCheckboxChange = (event) => { - const isCreateTlr = event.target.checked; - const { - form, - selectedItem, - selectedInstance, - onSetSelectedItem, - onSetSelectedInstance, - } = this.props; - - form.change(REQUEST_FORM_FIELD_NAMES.CREATE_TLR, isCreateTlr); - form.change(REQUEST_FORM_FIELD_NAMES.ITEM_BARCODE, null); - form.change(REQUEST_FORM_FIELD_NAMES.INSTANCE_HRID, null); - form.change(REQUEST_FORM_FIELD_NAMES.INSTANCE_ID, null); - - if (isCreateTlr) { - onSetSelectedItem(undefined); - this.setState({ - requestTypes: {}, - isRequestTypesReceived: false, - }); - - if (selectedItem) { - this.findInstance(null, selectedItem.holdingsRecordId); - } - } else if (selectedInstance) { - form.change(REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE, DEFAULT_REQUEST_TYPE_VALUE); - resetFieldState(form, REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE); - this.setState({ - isItemsDialogOpen: true, - }); - } else { - onSetSelectedInstance(undefined); - this.setState({ - requestTypes: {}, - isRequestTypesReceived: false, - }); - } - }; - - handleItemsDialogClose = () => { - const { - onSetSelectedInstance, - } = this.props; - - onSetSelectedInstance(undefined); - this.setState({ - isItemsDialogOpen: false, - requestTypes: {}, - isRequestTypesReceived: false, - isItemIdRequest: false, - }, this.triggerItemBarcodeValidation); - } - - handleInstanceItemClick = (event, item) => { - const { - onSetSelectedInstance, - } = this.props; - let isBarcodeRequired = false; - - onSetSelectedInstance(undefined); - this.setState({ - isItemsDialogOpen: false, - requestTypes: {}, - }); - - if (item?.barcode) { - isBarcodeRequired = true; - this.setState({ - isItemIdRequest: false, - }); - } - - this.findItem(RESOURCE_KEYS.id, item.id, false, isBarcodeRequired); - } - - handleCloseProxy = () => { - const { - onSetSelectedUser, - } = this.props; - - onSetSelectedUser(null); - this.setState({ - proxy: null, - }); - }; - - render() { - const { - handleSubmit, - request, - form, - optionLists: { - addressTypes, - }, - patronGroups, - parentResources, - submitting, - intl: { - formatMessage, - }, - errorMessage, - selectedItem, - selectedUser, - selectedInstance, - isPatronBlocksOverridden, - instanceId, - blocked, - values, - onCancel, - onGetAutomatedPatronBlocks, - onGetPatronManualBlocks, - isTlrEnabledOnEditPage, - optionLists, - pristine, - onSetSelectedItem, - onSetSelectedInstance, - metadataDisplay, - } = this.props; - - const { - selectedLoan, - itemRequestCount, - instanceRequestCount, - selectedAddressTypeId, - deliverySelected, - isCancellingRequest, - isUserLoading, - isItemOrInstanceLoading, - isAwaitingForProxySelection, - isItemsDialogOpen, - proxy, - requestTypes, - hasDelivery, - defaultDeliveryAddressTypeId, - isItemIdRequest, - isRequestTypesReceived, - isRequestTypeLoading, - } = this.state; - const { - createTitleLevelRequest, - } = values; - const patronBlocks = onGetPatronManualBlocks(parentResources); - const automatedPatronBlocks = onGetAutomatedPatronBlocks(parentResources); - const isEditForm = isFormEditing(request); - const selectedProxy = getProxy(request, proxy); - const requester = getRequester(selectedProxy, selectedUser); - let deliveryLocations; - let deliveryLocationsDetail = []; - let addressDetail; - if (requester?.personal?.addresses) { - deliveryLocations = requester.personal.addresses.map((a) => { - const typeName = find(addressTypes, { id: a.addressTypeId }).addressType; - return { label: typeName, value: a.addressTypeId }; - }); - deliveryLocations = sortBy(deliveryLocations, ['label']); - deliveryLocationsDetail = keyBy(requester.personal.addresses, a => a.addressTypeId); - } - - if (selectedAddressTypeId) { - addressDetail = toUserAddress(deliveryLocationsDetail[selectedAddressTypeId]); - } - - const patronGroup = getPatronGroup(requester, patronGroups); - const fulfillmentTypeOptions = getFulfillmentTypeOptions(hasDelivery, optionLists?.fulfillmentTypes || []); - const isSubmittingDisabled = isSubmittingButtonDisabled(pristine, submitting); - const isTitleLevelRequest = createTitleLevelRequest || request?.requestLevel === REQUEST_LEVEL_TYPES.TITLE; - const getPatronBlockModalOpenStatus = () => { - if (isAwaitingForProxySelection) { - return false; - } - - const isBlockedAndOverriden = blocked && !isPatronBlocksOverridden; - - return proxy?.id - ? isBlockedAndOverriden && (proxy.id === selectedUser?.id) - : isBlockedAndOverriden; - }; - - const handleCancelAndClose = () => { - const keepEditBtn = document.getElementById('clickable-cancel-editing-confirmation-confirm'); - if (isItemsDialogOpen) handleKeyCommand(this.handleItemsDialogClose); - else if (errorMessage) this.onClose(); - else if (keepEditBtn) keepEditBtn.click(); - else onCancel(); - }; - const isFulfilmentPreferenceVisible = (values.requestType || isEditForm) && !isRequestTypeLoading && isRequestTypesReceived; - const requestTypeOptions = Object.keys(requestTypes).map(requestType => { - return { - id: requestTypeOptionMap[requestType], - value: requestType, - }; - }); - - return ( - - -
- - : - } - footer={ - -
- - -
-
- } - > - { - errorMessage && - } - errorMessage={parseErrorMessage(errorMessage)} - /> - } - { - this.state.titleLevelRequestsFeatureEnabled && !isEditForm && -
- - - - - -
- } - - - { - isTitleLevelRequest - ? ( - } - > -
- -
-
- ) - : ( - } - > -
- -
-
- ) - } - } - > -
- -
-
- } - > - - {isFulfilmentPreferenceVisible && - - } - -
-
- this.setState({ isCancellingRequest: false })} - request={request} - stripes={this.props.stripes} - /> - this.onViewUserPath(selectedUser, patronGroup)} - patronBlocks={patronBlocks || []} - automatedPatronBlocks={automatedPatronBlocks} - /> - -
-
-
-
- ); - } -} - -export default stripesFinalForm({ - navigationCheck: true, - subscription: { - values: true, - }, -})(RequestForm); diff --git a/src/deprecated/components/RequestForm/RequestForm.test.js b/src/deprecated/components/RequestForm/RequestForm.test.js deleted file mode 100644 index 360ce320..00000000 --- a/src/deprecated/components/RequestForm/RequestForm.test.js +++ /dev/null @@ -1,1870 +0,0 @@ -import { Router } from 'react-router-dom'; -import { createMemoryHistory } from 'history'; - -import { - render, - screen, - fireEvent, - waitFor, -} from '@folio/jest-config-stripes/testing-library/react'; -import { - CommandList, - defaultKeyboardShortcuts, -} from '@folio/stripes/components'; - -import RequestForm, { - getRequestInformation, - getResourceTypeId, - ID_TYPE_MAP, -} from './RequestForm'; -import FulfilmentPreference from '../../../components/FulfilmentPreference'; -import RequesterInformation from '../../../components/RequesterInformation'; - -import { - REQUEST_LEVEL_TYPES, - createModes, - REQUEST_LAYERS, - REQUEST_FORM_FIELD_NAMES, - DEFAULT_REQUEST_TYPE_VALUE, - RESOURCE_KEYS, - REQUEST_OPERATIONS, -} from '../../../constants'; -import { RESOURCE_TYPES } from '../../constants'; -import { - getDefaultRequestPreferences, - isFormEditing, - getFulfillmentPreference, - resetFieldState, -} from '../../../utils'; -import { - getTlrSettings, - getRequester, -} from '../../utils'; - -const testIds = { - tlrCheckbox: 'tlrCheckbox', - instanceInfoSection: 'instanceInfoSection', - fulfilmentPreferenceField: 'fulfilmentPreferenceField', - addressFiled: 'addressFiled', - itemField: 'itemField', - requesterField: 'requesterField', - instanceField: 'instanceField', - selectProxyButton: 'selectProxyButton', - closeProxyButton: 'closeProxyButton', - overridePatronButton: 'overridePatronButton', - closePatronModalButton: 'closePatronModalButton', - itemDialogCloseButton: 'itemDialogCloseButton', - itemDialogRow: 'itemDialogRow', -}; -const fieldValue = 'value'; -const idResourceType = 'id'; -const instanceId = 'instanceId'; -const item = { - id: 'itemId', - barcode: 'itemBarcode', -}; - -jest.mock('../../../utils', () => ({ - ...jest.requireActual('../../../utils'), - getRequestLevelValue: jest.fn(), - resetFieldState: jest.fn(), - getDefaultRequestPreferences: jest.fn(), - isFormEditing: jest.fn(), - getFulfillmentPreference: jest.fn(), -})); -jest.mock('../../utils', () => ({ - getTlrSettings: jest.fn(() => ({ - titleLevelRequestsFeatureEnabled: true, - })), - getRequester: jest.fn((proxy, selectedUser) => selectedUser), -})); -jest.mock('../../../components/FulfilmentPreference', () => jest.fn(({ - changeDeliveryAddress, - onChangeAddress, -}) => { - const handleFulfilmentPreferences = () => { - changeDeliveryAddress(true); - }; - - return ( - <> - - - - ); -})); -jest.mock('../../../components/RequesterInformation', () => jest.fn(({ - findUser, -}) => { - const handleChange = () => { - findUser(idResourceType, fieldValue, true); - }; - - return ( - - ); -})); -jest.mock('../../../components/RequestInformation', () => jest.fn(() =>
)); -jest.mock('../ItemInformation/ItemInformation', () => jest.fn(({ - findItem, - triggerValidation, -}) => { - const handleChange = () => { - triggerValidation(); - findItem(idResourceType, fieldValue, true); - }; - - return ( - - ); -})); -jest.mock('../InstanceInformation/InstanceInformation', () => jest.fn(({ - findInstance, - triggerValidation, -}) => { - const handleChange = () => { - triggerValidation(); - findInstance(instanceId, null, true); - }; - - return ( - - ); -})); -jest.mock('@folio/stripes/final-form', () => () => jest.fn((component) => component)); -jest.mock('../../../PatronBlockModal', () => jest.fn(({ - onOverride, - onClose, -}) => ( - <> - - - -))); -jest.mock('../../../CancelRequestDialog', () => jest.fn(() =>
)); -jest.mock('../../../components/TitleInformation', () => jest.fn(() =>
)); -jest.mock('../../../ItemDetail', () => jest.fn(() =>
)); -jest.mock('../ItemsDialog/ItemsDialog', () => jest.fn(({ - onClose, - onRowClick, -}) => { - const handleRowClick = () => { - onRowClick({}, item); - }; - - return ( - <> - - - - ); -})); -jest.mock('../../../PositionLink', () => jest.fn(() =>
)); - -describe('RequestForm', () => { - const labelIds = { - tlrCheckbox: 'ui-requests.requests.createTitleLevelRequest', - instanceInformation: 'ui-requests.instance.information', - enterButton:'ui-requests.enter', - requestInfoAccordion: 'ui-requests.requestMeta.information', - requesterInfoAccordion: 'ui-requests.requester.information', - }; - const basicProps = { - onGetPatronManualBlocks: jest.fn(), - onGetAutomatedPatronBlocks: jest.fn(), - onSetSelectedInstance: jest.fn(), - onSetSelectedItem: jest.fn(), - onSetSelectedUser: jest.fn(), - onSetInstanceId: jest.fn(), - onSetIsPatronBlocksOverridden: jest.fn(), - onSetBlocked: jest.fn(), - onShowErrorModal: jest.fn(), - onHideErrorModal: jest.fn(), - onChangePatron: jest.fn(), - form: { - change: jest.fn(), - }, - handleSubmit: jest.fn(), - asyncValidate: jest.fn(), - initialValues: {}, - location: { - pathname: 'pathname', - }, - onCancel: jest.fn(), - parentMutator: { - proxy: { - reset: jest.fn(), - GET: jest.fn(), - }, - }, - onSubmit: jest.fn(), - parentResources: { - patronBlocks: { - records: [], - }, - automatedPatronBlocks: { - isPending: false, - }, - }, - intl: { - formatMessage: ({ id }) => id, - }, - stripes: { - connect: jest.fn((component) => component), - store: { - getState: jest.fn(), - }, - }, - values: { - deliveryAddressTypeId: '', - pickupServicePointId: '', - createTitleLevelRequest: '', - }, - findResource: jest.fn(() => new Promise((resolve) => resolve())), - request: {}, - query: {}, - selectedItem: {}, - selectedUser: {}, - selectedInstance: {}, - isPatronBlocksOverridden: false, - isErrorModalOpen: false, - blocked: false, - optionLists: { - addressTypes: [ - { - id: 'addressTypeId', - addressType: 'Home', - } - ], - }, - }; - const defaultPreferences = { - defaultServicePointId: 'defaultServicePointId', - defaultDeliveryAddressTypeId: 'defaultDeliveryAddressTypeId', - }; - const fulfillmentPreference = {}; - const renderComponent = (passedProps = basicProps) => { - const history = createMemoryHistory(); - const { rerender } = render( - - - - - - ); - - return rerender; - }; - - getDefaultRequestPreferences.mockReturnValue(defaultPreferences); - getFulfillmentPreference.mockReturnValue(fulfillmentPreference); - - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('when `TLR` in enabled', () => { - describe('when `isEdit` is false', () => { - beforeEach(() => { - isFormEditing.mockReturnValue(false); - }); - - describe('Initial render', () => { - const holding = { - holdingsRecords: [ - { - instanceId: 'instanceId', - } - ], - }; - const selectedItem = { - holdingsRecordId: 'holdingsRecordId', - }; - let findResource; - - beforeEach(() => { - findResource = jest.fn() - .mockResolvedValueOnce(holding) - .mockResolvedValueOnce(null); - - const props = { - ...basicProps, - selectedItem, - findResource, - }; - - renderComponent(props); - }); - - it('should render `TLR` checkbox section', () => { - const tlrCheckbox = screen.getByTestId(testIds.tlrCheckbox); - - expect(tlrCheckbox).toBeVisible(); - }); - - it('should reset instance and item info on TLR checkbox change', () => { - const expectedArgs = [ - ['item.barcode', null], - ['instance.hrid', null], - ['instanceId', null] - ]; - const tlrCheckbox = screen.getByTestId(testIds.tlrCheckbox); - - fireEvent.click(tlrCheckbox); - - expectedArgs.forEach(args => { - expect(basicProps.form.change).toBeCalledWith(...args); - }); - }); - - it('should reset selected item', () => { - const tlrCheckbox = screen.getByTestId(testIds.tlrCheckbox); - - fireEvent.click(tlrCheckbox); - - expect(basicProps.onSetSelectedItem).toHaveBeenCalledWith(undefined); - }); - - it('should get instance id if it is not provided', () => { - const expectedArgs = [RESOURCE_TYPES.HOLDING, selectedItem.holdingsRecordId]; - const tlrCheckbox = screen.getByTestId(testIds.tlrCheckbox); - - fireEvent.click(tlrCheckbox); - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('`TLR` checkbox handle on first render', () => { - describe('when only `itemId` is passed in query', () => { - const props = { - ...basicProps, - query: { - itemId: 'itemId', - } - }; - - it('should set form `createTitleLevelRequest` value to false', () => { - const expectedArgs = ['createTitleLevelRequest', false]; - - renderComponent(props); - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('when only `itemBarcode` is passed in query', () => { - const props = { - ...basicProps, - query: { - itemBarcode: 'itemBarcode', - } - }; - - it('should set form `createTitleLevelRequest` value to false', () => { - const expectedArgs = ['createTitleLevelRequest', false]; - - renderComponent(props); - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('when only `instanceId` is passed in query', () => { - const props = { - ...basicProps, - query: { - instanceId: 'instanceId', - } - }; - - it('should set form `createTitleLevelRequest` value to true', () => { - const expectedArgs = ['createTitleLevelRequest', true]; - - renderComponent(props); - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - }); - - describe('when `TLR` checkbox is checked', () => { - const props = { - ...basicProps, - values: { - ...basicProps.values, - createTitleLevelRequest: true, - }, - }; - - beforeEach(() => { - renderComponent(props); - }); - - it('should render Accordion with `Instance` information', () => { - const instanceInformation = screen.getByText(labelIds.instanceInformation); - - expect(instanceInformation).toBeVisible(); - }); - }); - - describe('when `TLR` checkbox is unchecked', () => { - const props = { - ...basicProps, - values: { - ...basicProps.values, - createTitleLevelRequest: false, - }, - }; - - it('should not render Accordion with `Instance` information', () => { - renderComponent(props); - - const instanceInformation = screen.queryByText(labelIds.instanceInformation); - - expect(instanceInformation).not.toBeInTheDocument(); - }); - }); - }); - - describe('when `isEdit` is true', () => { - const mockedInstance = { - id: 'instanceId', - title: 'instanceTitle', - contributors: 'instanceContributors', - publication: 'instancePublication', - editions: 'instanceEditions', - identifiers: 'instanceIdentifiers', - }; - const mockedRequest = { - instance : mockedInstance, - id : 'testId', - instanceId : 'instanceId', - requestType: 'Hold', - status: 'Open - Awaiting delivery', - }; - - beforeEach(() => { - isFormEditing.mockReturnValue(true); - }); - - it('should not render `TLR` checkbox section', () => { - const props = { - ...basicProps, - request: mockedRequest, - }; - - renderComponent(props); - - const tlrCheckbox = screen.queryByTestId(testIds.tlrCheckbox); - - expect(tlrCheckbox).not.toBeInTheDocument(); - }); - }); - }); - - describe('when `TLR` is disabled', () => { - beforeEach(() => { - getTlrSettings.mockReturnValue({ - titleLevelRequestsFeatureEnabled: false, - }); - renderComponent(); - }); - - it('should not display `TLR` checkbox', () => { - const tlrCheckbox = screen.queryByTestId(testIds.tlrCheckbox); - - expect(tlrCheckbox).not.toBeInTheDocument(); - }); - - it('should set form `createTitleLevelRequest` value to false', () => { - const expectedArgs = ['createTitleLevelRequest', false]; - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('when duplicate a request', () => { - const initialProps = { - ...basicProps, - query: { - mode: createModes.DUPLICATE, - }, - selectedUser: { - id: 'userId', - }, - request: { - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - }, - initialValues: { - item: { - id: 'itemId', - }, - }, - }; - const findResource = jest.fn().mockResolvedValue({}); - const newProps = { - ...initialProps, - selectedInstance: { - id: 'instanceId', - }, - selectedItem: null, - findResource, - }; - - beforeEach(() => { - const rerender = renderComponent(initialProps); - - isFormEditing.mockReturnValue(false); - - rerender( - - - - - - ); - }); - - it('should trigger "findResource" to find request types', () => { - expect(findResource).toHaveBeenCalledWith(RESOURCE_TYPES.REQUEST_TYPES, { - [ID_TYPE_MAP.INSTANCE_ID]: newProps.selectedInstance.id, - requesterId: newProps.selectedUser.id, - operation: REQUEST_OPERATIONS.CREATE, - }); - }); - - it('should set selected item', () => { - expect(basicProps.onSetSelectedItem).toHaveBeenCalledWith(initialProps.initialValues.item); - }); - - it('should trigger item barcode field validation', () => { - const expectedArgs = ['keyOfItemBarcodeField', expect.any(Number)]; - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('Request information', () => { - const requesterId = 'requesterId'; - - beforeEach(() => { - const newProps = { - ...basicProps, - query: { - layer: REQUEST_LAYERS.EDIT, - }, - request: { - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - requester: {}, - requesterId, - instanceId, - }, - values: { - ...basicProps.values, - requestType: 'requestType', - createTitleLevelRequest: true, - }, - selectedUser: { - id: 'userId', - personal: { - addresses: [ - { - addressTypeId: basicProps.optionLists.addressTypes[0].id, - } - ], - }, - }, - }; - const rerender = renderComponent(); - - rerender( - - - - - - ); - }); - - it('should render request information accordion', () => { - const requestInfoAccordion = screen.getByText(labelIds.requestInfoAccordion); - - expect(requestInfoAccordion).toBeInTheDocument(); - }); - - it('should trigger "FulfilmentPreference" with provided delivery locations', async () => { - const expectedProps = { - deliveryLocations: [ - { - label: basicProps.optionLists.addressTypes[0].addressType, - value: basicProps.optionLists.addressTypes[0].id, - } - ], - }; - - await waitFor(() => { - expect(FulfilmentPreference).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - }); - - it('should handle changing of address field', async () => { - const addressField = await screen.findByTestId(testIds.addressFiled); - const event = { - target: { - value: 'selectedAddressTypeId', - }, - }; - const expectedArgs = [REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, event.target.value]; - - fireEvent.change(addressField, event); - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should handle changing of fulfilment preferences field', async () => { - const fulfilmentPreferenceField = await screen.findByTestId(testIds.fulfilmentPreferenceField); - const event = { - target: { - value: 'fulfilmentPreferences', - }, - }; - const expectedArgs = [ - [REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, defaultPreferences.defaultDeliveryAddressTypeId], - [REQUEST_FORM_FIELD_NAMES.PICKUP_SERVICE_POINT_ID, ''] - ]; - - fireEvent.change(fulfilmentPreferenceField, event); - - expectedArgs.forEach(args => { - expect(basicProps.form.change).toHaveBeenCalledWith(...args); - }); - }); - - it('should trigger "form.change" with correct arguments', () => { - const expectedArgs = [REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE, DEFAULT_REQUEST_TYPE_VALUE]; - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should trigger "findResource" with correct arguments', () => { - const expectedArgs = [ - RESOURCE_TYPES.REQUEST_TYPES, - { - [ID_TYPE_MAP.INSTANCE_ID]: instanceId, - requesterId, - operation: REQUEST_OPERATIONS.CREATE, - } - ]; - - expect(basicProps.findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('Requester information', () => { - const requestPreferencesResult = { - requestPreferences: [ - { - delivery: true, - } - ], - }; - const manualBlocks = [ - { - userId: 'id', - } - ]; - const userResult = { - totalRecords: 1, - users: [ - { - id: 'userId', - } - ], - }; - - beforeEach(() => { - isFormEditing.mockReturnValue(false); - basicProps.onGetPatronManualBlocks.mockReturnValue(manualBlocks); - }); - - describe('When userId is presented', () => { - const initialUserId = 'userId'; - const updatedUserId = 'updatedUserId'; - - describe('Initial rendering', () => { - const automatedBlocks = [{}]; - const userProxies = []; - let findResource; - - beforeEach(() => { - basicProps.onGetAutomatedPatronBlocks.mockReturnValue(automatedBlocks); - basicProps.parentMutator.proxy.GET.mockResolvedValue(userProxies); - findResource = jest.fn() - .mockResolvedValueOnce(userResult) - .mockResolvedValueOnce(requestPreferencesResult) - .mockResolvedValue({}); - - const props = { - ...basicProps, - findResource, - query: { - layer: REQUEST_LAYERS.CREATE, - userId: initialUserId, - }, - }; - - renderComponent(props); - }); - - it('should render requester information accordion', () => { - const requesterInfoAccordion = screen.getByText(labelIds.requesterInfoAccordion); - - expect(requesterInfoAccordion).toBeInTheDocument(); - }); - - it('should reset user related fields', () => { - const expectedArgs = [ - [REQUEST_FORM_FIELD_NAMES.PICKUP_SERVICE_POINT_ID, undefined], - [REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, undefined], - [REQUEST_FORM_FIELD_NAMES.PROXY_USER_ID, undefined] - ]; - - expectedArgs.forEach(args => { - expect(basicProps.form.change).toHaveBeenCalledWith(...args); - }); - }); - - it('should set user related information', () => { - const expectedArgs = [ - [REQUEST_FORM_FIELD_NAMES.REQUESTER_ID, userResult.users[0].id], - [REQUEST_FORM_FIELD_NAMES.REQUESTER, userResult.users[0]] - ]; - - expectedArgs.forEach(args => { - expect(basicProps.form.change).toHaveBeenCalledWith(...args); - }); - }); - - it('should get user data using user id', () => { - const expectedArgs = [ - RESOURCE_TYPES.USER, - initialUserId, - RESOURCE_KEYS.id, - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should get manual blocks information', () => { - expect(basicProps.onGetPatronManualBlocks).toHaveBeenCalledWith(basicProps.parentResources); - }); - - it('should get automated blocks information', () => { - expect(basicProps.onGetAutomatedPatronBlocks).toHaveBeenCalledWith(basicProps.parentResources); - }); - - it('should change patron information', () => { - expect(basicProps.onChangePatron).toHaveBeenCalledWith(userResult.users[0]); - }); - - it('should set selected user', () => { - expect(basicProps.onSetSelectedUser).toHaveBeenCalledWith(userResult.users[0]); - }); - - it('should trigger validation of user barcode field', () => { - const expectedArg = ['keyOfUserBarcodeField', 1]; - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArg); - }); - - it('should get request preferences data', () => { - const expectedArgs = [ - 'requestPreferences', - userResult.users[0].id, - 'userId', - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should set fulfilment preference information', async () => { - const expectedArgs = [ - REQUEST_FORM_FIELD_NAMES.FULFILLMENT_PREFERENCE, - fulfillmentPreference - ]; - - await waitFor(() => { - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - it('should set blocked to true', () => { - expect(basicProps.onSetBlocked).toHaveBeenCalledWith(true); - }); - - it('should set isPatronBlocksOverridden to false', () => { - expect(basicProps.onSetIsPatronBlocksOverridden).toHaveBeenCalledWith(false); - }); - - it('should set default service point', async () => { - const expectedArgs = [ - REQUEST_FORM_FIELD_NAMES.PICKUP_SERVICE_POINT_ID, - defaultPreferences.defaultServicePointId - ]; - - await waitFor(() => { - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - it('should reset delivery address type id', async () => { - const expectedArgs = [ - REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, - '' - ]; - - await waitFor(() => { - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - it('should reset proxy information', () => { - expect(basicProps.parentMutator.proxy.reset).toHaveBeenCalled(); - }); - - it('should get proxy information', () => { - const expectedArg = { - params: { - query: `query=(proxyUserId==${userResult.users[0].id})`, - }, - }; - - expect(basicProps.parentMutator.proxy.GET).toHaveBeenCalledWith(expectedArg); - }); - - it('should handle requester barcode field change', () => { - const expectedArgs = [RESOURCE_TYPES.USER, fieldValue, RESOURCE_KEYS.id]; - const requesterField = screen.getByTestId(testIds.requesterField); - const event = { - target: { - value: 'value', - }, - }; - - fireEvent.change(requesterField, event); - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('Component updating', () => { - const findResource = jest.fn(() => Promise.resolve({})); - const props = { - ...basicProps, - query: { - layer: REQUEST_LAYERS.CREATE, - userId: initialUserId, - }, - findResource, - }; - const newProps = { - ...basicProps, - values: {}, - request: {}, - query: { - layer: REQUEST_LAYERS.CREATE, - userId: updatedUserId, - }, - findResource, - }; - - beforeEach(() => { - const rerender = renderComponent(props); - - rerender( - - - - - - ); - }); - - it('should get user data by user id', () => { - const expectedArgs = [ - RESOURCE_TYPES.USER, - updatedUserId, - RESOURCE_KEYS.id, - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('Proxy handling', () => { - const selectedUser = { - id: 'userId', - }; - let findResource; - - beforeEach(() => { - const automatedBlocks = []; - - findResource = jest.fn() - .mockResolvedValueOnce(userResult) - .mockResolvedValueOnce(requestPreferencesResult); - basicProps.onGetAutomatedPatronBlocks.mockReturnValue(automatedBlocks); - }); - - describe('When user acts as a proxy', () => { - const proxy = { - id: 'proxyId', - }; - - beforeEach(() => { - const props = { - ...basicProps, - query: { - layer: REQUEST_LAYERS.CREATE, - userId: initialUserId, - }, - selectedUser, - findResource, - }; - const userProxies = [ - { - ...proxy, - } - ]; - - RequesterInformation.mockImplementation(({ - onSelectProxy, - handleCloseProxy, - }) => { - const handleSelectProxy = () => { - onSelectProxy(proxy); - }; - - return ( - <> - - - - ); - }); - basicProps.parentMutator.proxy.GET.mockResolvedValue(userProxies); - - renderComponent(props); - }); - - it('should set selected user', () => { - const selectProxyButton = screen.getByTestId(testIds.selectProxyButton); - - fireEvent.click(selectProxyButton); - - expect(basicProps.onSetSelectedUser).toHaveBeenCalledWith(selectedUser); - }); - - it('should change requester related fields', () => { - const expectedArgs = [ - [REQUEST_FORM_FIELD_NAMES.REQUESTER_ID, proxy.id], - [REQUEST_FORM_FIELD_NAMES.PROXY_USER_ID, selectedUser.id] - ]; - const selectProxyButton = screen.getByTestId(testIds.selectProxyButton); - - fireEvent.click(selectProxyButton); - - expectedArgs.forEach(args => { - expect(basicProps.form.change).toHaveBeenCalledWith(...args); - }); - }); - - it('should set selected user to null', () => { - const closeProxyButton = screen.getByTestId(testIds.closeProxyButton); - - fireEvent.click(closeProxyButton); - - expect(basicProps.onSetSelectedUser).toHaveBeenCalledWith(null); - }); - }); - - describe('When user acts as himself', () => { - const proxy = { - id: selectedUser.id, - }; - - beforeEach(() => { - const props = { - ...basicProps, - query: { - layer: REQUEST_LAYERS.CREATE, - userId: initialUserId, - }, - selectedUser, - findResource, - }; - const userProxies = [ - { - ...proxy, - } - ]; - - RequesterInformation.mockImplementation(({ - onSelectProxy, - }) => { - const handleSelectProxy = () => { - onSelectProxy(proxy); - }; - - return ( - <> - - - ); - }); - basicProps.parentMutator.proxy.GET.mockResolvedValue(userProxies); - - renderComponent(props); - }); - - it('should change requester related fields', () => { - const expectedArgs = [REQUEST_FORM_FIELD_NAMES.REQUESTER_ID, selectedUser.id]; - const selectProxyButton = screen.getByTestId(testIds.selectProxyButton); - - fireEvent.click(selectProxyButton); - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - }); - }); - - describe('When userBarcode is presented', () => { - const initialUserBarcode = 'userBarcode'; - const updatedUserBarcode = 'updatedUserBarcode'; - - describe('Initial rendering', () => { - const automatedBlocks = []; - const userProxies = [{}]; - let findResource; - - beforeEach(() => { - basicProps.onGetAutomatedPatronBlocks.mockReturnValue(automatedBlocks); - basicProps.parentMutator.proxy.GET.mockResolvedValue(userProxies); - findResource = jest.fn() - .mockResolvedValueOnce(userResult) - .mockResolvedValueOnce(requestPreferencesResult); - - const props = { - ...basicProps, - query: { - layer: REQUEST_LAYERS.CREATE, - userBarcode: initialUserBarcode, - }, - findResource, - }; - - renderComponent(props); - }); - - it('should reset user related fields', () => { - const expectedArgs = [ - [REQUEST_FORM_FIELD_NAMES.PICKUP_SERVICE_POINT_ID, undefined], - [REQUEST_FORM_FIELD_NAMES.DELIVERY_ADDRESS_TYPE_ID, undefined], - [REQUEST_FORM_FIELD_NAMES.PROXY_USER_ID, undefined] - ]; - - expectedArgs.forEach(args => { - expect(basicProps.form.change).toHaveBeenCalledWith(...args); - }); - }); - - it('should get user data using barcode', () => { - const expectedArgs = [ - RESOURCE_TYPES.USER, - initialUserBarcode, - RESOURCE_KEYS.barcode, - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should not trigger validation of user barcode field', () => { - const expectedArg = ['keyOfUserBarcodeField', expect.any(Number)]; - - expect(basicProps.form.change).not.toHaveBeenCalledWith(...expectedArg); - }); - - it('should not set blocked to true', () => { - expect(basicProps.onSetBlocked).not.toHaveBeenCalledWith(true); - }); - - it('should not set isPatronBlocksOverridden to false', () => { - expect(basicProps.onSetIsPatronBlocksOverridden).not.toHaveBeenCalledWith(false); - }); - }); - - describe('Component updating', () => { - const findResource = jest.fn(() => Promise.resolve({})); - const props = { - ...basicProps, - findResource, - query: { - layer: REQUEST_LAYERS.CREATE, - userBarcode: initialUserBarcode, - }, - }; - const newProps = { - ...basicProps, - findResource, - query: { - layer: REQUEST_LAYERS.CREATE, - userBarcode: updatedUserBarcode, - }, - }; - - beforeEach(() => { - const rerender = renderComponent(props); - - rerender( - - - - - - ); - }); - - it('should get user data by user barcode', () => { - const expectedArgs = [ - RESOURCE_TYPES.USER, - updatedUserBarcode, - RESOURCE_KEYS.barcode, - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - }); - }); - - describe('Item information', () => { - describe('When item barcode is presented', () => { - const initialItemBarcode = 'itemBarcode'; - const updatedItemBarcode = 'updatedItemBarcode'; - - describe('Initial render', () => { - const requestPreferencesResult = {}; - - beforeEach(() => { - isFormEditing.mockReturnValue(true); - }); - - describe('When item is found', () => { - const event = { - target: { - value: 'barcode', - }, - }; - const itemResult = { - totalRecords: 1, - items: [ - { - id: 'itemId', - barcode: initialItemBarcode, - holdingsRecordId: 'holdingsRecordId', - } - ], - }; - const requestTypesResult = { - 'Page': [ - { - id: 'id', - name: 'Circ Desk 1', - } - ] - }; - const loanResult = { - loans: [ - { - id: 'loanId', - } - ], - }; - const itemRequestsResult = { - requests: [], - }; - const holdingsRecordResult = { - holdingsRecords: [ - { - instanceId, - } - ], - }; - let findResource; - - beforeEach(() => { - findResource = jest.fn() - .mockResolvedValueOnce(itemResult) - .mockResolvedValueOnce(requestPreferencesResult) - .mockResolvedValueOnce(requestTypesResult) - .mockResolvedValueOnce(loanResult) - .mockResolvedValueOnce(itemRequestsResult) - .mockResolvedValueOnce(holdingsRecordResult) - .mockResolvedValue({}); - - const props = { - ...basicProps, - selectedUser: { - id: 'selectedUserId', - }, - query: { - itemBarcode: initialItemBarcode, - }, - request: { - id: 'requestId', - }, - findResource, - }; - - renderComponent(props); - }); - - it('should get information about requested item', () => { - const expectedArgs = [ - RESOURCE_TYPES.ITEM, - initialItemBarcode, - RESOURCE_KEYS.barcode - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should set item information', () => { - const expectedArgs = [ - [REQUEST_FORM_FIELD_NAMES.ITEM_ID, itemResult.items[0].id], - [REQUEST_FORM_FIELD_NAMES.ITEM_BARCODE, itemResult.items[0].barcode] - ]; - - expectedArgs.forEach(args => { - expect(basicProps.form.change).toHaveBeenCalledWith(...args); - }); - }); - - it('should reset field state for request type', () => { - const expectedArgs = [basicProps.form, REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE]; - - expect(resetFieldState).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should get requester information', () => { - expect(getRequester).toHaveBeenCalled(); - }); - - it('should get information about loans', () => { - const expectedArgs = [ - 'loan', - itemResult.items[0].id - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should get information about open item requests', () => { - const expectedArgs = [ - 'requestsForItem', - itemResult.items[0].id - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should get information about holdings', () => { - const expectedArgs = [ - RESOURCE_TYPES.HOLDING, - itemResult.items[0].holdingsRecordId - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should set instance id', () => { - expect(basicProps.onSetInstanceId).toHaveBeenCalledWith(holdingsRecordResult.holdingsRecords[0].instanceId); - }); - - it('should handle item barcode field change', () => { - const expectedArgs = [RESOURCE_TYPES.ITEM, fieldValue, RESOURCE_KEYS.id]; - const itemField = screen.getByTestId(testIds.itemField); - - fireEvent.change(itemField, event); - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should trigger item barcode field validation', () => { - const expectedArgs = ['keyOfItemBarcodeField', expect.any(Number)]; - const itemField = screen.getByTestId(testIds.itemField); - - fireEvent.change(itemField, event); - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('When item is not found', () => { - const itemResult = { - totalRecords: 0, - items: [], - }; - let findResource; - - beforeEach(() => { - findResource = jest.fn() - .mockResolvedValueOnce(itemResult) - .mockResolvedValueOnce(requestPreferencesResult); - - const props = { - ...basicProps, - selectedUser: { - id: 'selectedUserId', - }, - query: { - itemBarcode: initialItemBarcode, - }, - request: { - id: 'requestId', - }, - findResource, - }; - - renderComponent(props); - }); - - it('should get information about requested item', () => { - const expectedArgs = [ - RESOURCE_TYPES.ITEM, - initialItemBarcode, - RESOURCE_KEYS.barcode - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should not reset field state for request type', () => { - const expectedArgs = [basicProps.form, REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE]; - - expect(resetFieldState).not.toHaveBeenCalledWith(...expectedArgs); - }); - - it('should not get request types information', () => { - const expectedArgs = [RESOURCE_TYPES.REQUEST_TYPES, expect.any(Object)]; - - expect(findResource).not.toHaveBeenCalledWith(...expectedArgs); - }); - }); - }); - - describe('Component updating', () => { - const findResource = jest.fn(() => Promise.resolve()); - const props = { - ...basicProps, - query: { - itemBarcode: initialItemBarcode, - }, - findResource, - }; - const newProps = { - ...basicProps, - query: { - itemBarcode: updatedItemBarcode, - }, - findResource, - }; - - beforeEach(() => { - const rerender = renderComponent(props); - - rerender( - - - - - - ); - }); - - it('should get item data by item barcode', () => { - const expectedArgs = [ - RESOURCE_TYPES.ITEM, - updatedItemBarcode, - RESOURCE_KEYS.barcode, - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - }); - - describe('When item id is presented', () => { - const initialItemId = 'itemId'; - const updatedItemId = 'updatedItemId'; - const itemResult = { - totalRecords: 0, - items: [], - }; - - describe('Initial render', () => { - const findResource = jest.fn().mockResolvedValueOnce(itemResult); - const props = { - ...basicProps, - query: { - itemId: initialItemId, - }, - findResource, - }; - - beforeEach(() => { - renderComponent(props); - }); - - it('should get information about requested item by item id', () => { - const expectedArgs = [ - RESOURCE_TYPES.ITEM, - initialItemId, - RESOURCE_KEYS.id - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('Component updating', () => { - const findResource = jest.fn().mockResolvedValue(itemResult); - const props = { - ...basicProps, - query: { - itemId: initialItemId, - }, - findResource, - }; - const newProps = { - ...basicProps, - query: { - itemId: updatedItemId, - }, - findResource, - }; - - beforeEach(() => { - const rerender = renderComponent(props); - - rerender( - - - - - - ); - }); - - it('should get information about requested item after component updating', () => { - const expectedArgs = [ - RESOURCE_TYPES.ITEM, - updatedItemId, - RESOURCE_KEYS.id - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - }); - }); - - describe('Instance information', () => { - const initialInstanceId = 'instanceId'; - const updatedInstanceId = 'updatedInstanceId'; - - describe('Initial render', () => { - describe('When instance is found', () => { - const event = { - target: { - value: 'value', - }, - }; - const instanceResult = { - totalRecords: 1, - instances: [ - { - id: initialInstanceId, - hrid: 'hrid', - } - ], - }; - const requestTypesResult = { - 'Page': [ - { - id: 'id', - name: 'Circ Desk 1', - } - ] - }; - const instanceRequestsResult = { - requests: [ - { - requestLevel: REQUEST_LEVEL_TYPES.ITEM, - } - ], - }; - let findResource; - - beforeEach(() => { - findResource = jest.fn() - .mockResolvedValueOnce(instanceResult) - .mockResolvedValueOnce(requestTypesResult) - .mockResolvedValue(instanceRequestsResult); - - const props = { - ...basicProps, - selectedUser: { - id: 'selectedUserId', - }, - query: { - instanceId: initialInstanceId, - }, - request: { - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - }, - findResource, - }; - - renderComponent(props); - }); - - it('should get information about requested instance', () => { - const expectedArgs = [ - RESOURCE_TYPES.INSTANCE, - initialInstanceId, - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should set instance information', () => { - const expectedArgs = [ - [REQUEST_FORM_FIELD_NAMES.INSTANCE_ID, instanceResult.instances[0].id], - [REQUEST_FORM_FIELD_NAMES.INSTANCE_HRID, instanceResult.instances[0].hrid] - ]; - - expectedArgs.forEach(args => { - expect(basicProps.form.change).toHaveBeenCalledWith(...args); - }); - }); - - it('should reset field state for request type', () => { - const expectedArgs = [basicProps.form, REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE]; - - expect(resetFieldState).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should get requester information', () => { - expect(getRequester).toHaveBeenCalled(); - }); - - it('should get information about open instance requests', () => { - const expectedArgs = [ - 'requestsForInstance', - instanceResult.instances[0].id - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should set selected instance', () => { - expect(basicProps.onSetSelectedInstance).toHaveBeenCalledWith(instanceResult.instances[0]); - }); - - it('should handle instance id field change', () => { - const expectedArgs = [RESOURCE_TYPES.INSTANCE, instanceId]; - const instanceField = screen.getByTestId(testIds.instanceField); - - fireEvent.change(instanceField, event); - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should trigger instance id field validation', () => { - const expectedArgs = ['keyOfInstanceIdField', expect.any(Number)]; - const instanceField = screen.getByTestId(testIds.instanceField); - - fireEvent.change(instanceField, event); - - expect(basicProps.form.change).toHaveBeenCalledWith(...expectedArgs); - }); - }); - - describe('When instance is not found', () => { - const instanceResult = { - totalRecords: 0, - instances: [], - }; - let findResource; - - beforeEach(() => { - findResource = jest.fn().mockResolvedValueOnce(instanceResult); - - const props = { - ...basicProps, - query: { - instanceId: initialInstanceId, - }, - findResource, - }; - - renderComponent(props); - }); - - it('should get information about requested instance', () => { - const expectedArgs = [ - RESOURCE_TYPES.INSTANCE, - initialInstanceId, - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should not get request types information', () => { - const expectedArgs = [RESOURCE_TYPES.REQUEST_TYPES, expect.any(Object)]; - - expect(findResource).not.toHaveBeenCalledWith(...expectedArgs); - }); - - it('should not reset field state for request type', () => { - const expectedArgs = [basicProps.form, REQUEST_FORM_FIELD_NAMES.REQUEST_TYPE]; - - expect(resetFieldState).not.toHaveBeenCalledWith(...expectedArgs); - }); - }); - }); - - describe('Component updating', () => { - const findResource = jest.fn(() => Promise.resolve()); - const props = { - ...basicProps, - findResource, - query: { - instanceId: initialInstanceId, - }, - }; - const newProps = { - ...basicProps, - findResource, - query: { - instanceId: updatedInstanceId, - }, - }; - - beforeEach(() => { - const rerender = renderComponent(props); - - rerender( - - - - - - ); - }); - - it('should get information about requested instance after component updating', () => { - const expectedArgs = [ - RESOURCE_TYPES.INSTANCE, - updatedInstanceId - ]; - - expect(findResource).toHaveBeenCalledWith(...expectedArgs); - }); - }); - }); - - describe('Patron block modal', () => { - beforeEach(() => { - renderComponent(); - }); - - it('should set isPatronBlocksOverridden to true', () => { - const overridePatronButton = screen.getByTestId(testIds.overridePatronButton); - - fireEvent.click(overridePatronButton); - - expect(basicProps.onSetIsPatronBlocksOverridden).toHaveBeenCalledWith(true); - }); - - it('should set blocked to false', () => { - const closePatronModalButton = screen.getByTestId(testIds.closePatronModalButton); - - fireEvent.click(closePatronModalButton); - - expect(basicProps.onSetBlocked).toHaveBeenCalledWith(false); - }); - }); - - describe('Items dialog', () => { - beforeEach(() => { - renderComponent(); - }); - - it('should get information about selected item', () => { - const expectedArgs = [RESOURCE_TYPES.ITEM, item.id, RESOURCE_KEYS.id]; - const itemDialogRow = screen.getByTestId(testIds.itemDialogRow); - - fireEvent.click(itemDialogRow); - - expect(basicProps.findResource).toHaveBeenCalledWith(...expectedArgs); - }); - - it('should reset selected instance', () => { - const itemDialogCloseButton = screen.getByTestId(testIds.itemDialogCloseButton); - - fireEvent.click(itemDialogCloseButton); - - expect(basicProps.onSetSelectedInstance).toHaveBeenCalledWith(undefined); - }); - }); - - describe('getResourceTypeId', () => { - it('should return instance id type', () => { - expect(getResourceTypeId(true)).toBe(ID_TYPE_MAP.INSTANCE_ID); - }); - - it('should return item id type', () => { - expect(getResourceTypeId(false)).toBe(ID_TYPE_MAP.ITEM_ID); - }); - }); - - describe('getRequestInformation', () => { - describe('when title level request', () => { - const selectedInstance = { - id: 'instanceId', - }; - const args = [ - {}, - selectedInstance, - {}, - { - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - }, - ]; - - it('should return correct data', () => { - const expectedResult = { - isTitleLevelRequest: true, - selectedResource: selectedInstance, - }; - - expect(getRequestInformation(...args)).toEqual(expectedResult); - }); - }); - - describe('when item level request', () => { - const selectedItem = { - id: 'itemId', - }; - const args = [ - {}, - {}, - selectedItem, - { - requestLevel: REQUEST_LEVEL_TYPES.ITEM, - }, - ]; - - it('should return correct data', () => { - const expectedResult = { - isTitleLevelRequest: false, - selectedResource: selectedItem, - }; - - expect(getRequestInformation(...args)).toEqual(expectedResult); - }); - }); - }); -}); diff --git a/src/deprecated/components/RequestFormContainer/RequestFormContainer.js b/src/deprecated/components/RequestFormContainer/RequestFormContainer.js deleted file mode 100644 index 4b60e9d7..00000000 --- a/src/deprecated/components/RequestFormContainer/RequestFormContainer.js +++ /dev/null @@ -1,202 +0,0 @@ -import { - useState, -} from 'react'; -import { - useIntl, -} from 'react-intl'; -import { - cloneDeep, - isEmpty, - isString, - unset, -} from 'lodash'; -import moment from 'moment-timezone'; -import PropTypes from 'prop-types'; - -import RequestForm from '../RequestForm/RequestForm'; -import { - getRequestLevelValue, -} from '../../../utils'; -import { - fulfillmentTypeMap, - REQUEST_LEVEL_TYPES, -} from '../../../constants'; -import { RESOURCE_TYPES } from '../../constants'; - -const RequestFormContainer = ({ - parentResources, - request, - onSubmit, - ...rest -}) => { - const { - requester, - requesterId, - item, - } = request || {}; - const intl = useIntl(); - const [selectedItem, setSelectedItem] = useState(item); - const [selectedUser, setSelectedUser] = useState({ ...requester, id: requesterId }); - const [selectedInstance, setSelectedInstance] = useState(request?.instance); - const [isPatronBlocksOverridden, setIsPatronBlocksOverridden] = useState(false); - const [instanceId, setInstanceId] = useState(''); - const [blocked, setBlocked] = useState(false); - - const setItem = (optedItem) => { - setSelectedItem(optedItem); - }; - - const setUser = (user) => { - setSelectedUser(user); - }; - - const setInstance = (instance) => { - setSelectedInstance(instance); - }; - - const setIsBlocked = (value) => { - setBlocked(value); - }; - - const setStateIsPatronBlocksOverridden = (value) => { - setIsPatronBlocksOverridden(value); - }; - - const setStateInstanceId = (id) => { - setInstanceId(id); - }; - - const getPatronManualBlocks = (resources) => { - return (resources?.patronBlocks?.records || []) - .filter(b => b.requests === true) - .filter(p => moment(moment(p.expirationDate).format()).isSameOrAfter(moment().format())); - }; - - const getAutomatedPatronBlocks = (resources) => { - const automatedPatronBlocks = resources?.automatedPatronBlocks?.records || []; - - return automatedPatronBlocks.reduce((blocks, block) => { - if (block.blockRequests) { - blocks.push(block.message); - } - - return blocks; - }, []); - }; - - const hasBlocking = () => { - const [block = {}] = getPatronManualBlocks(parentResources); - const automatedPatronBlocks = getAutomatedPatronBlocks(parentResources); - const isBlocked = ( - (block?.userId === selectedUser.id || !isEmpty(automatedPatronBlocks)) && - !isPatronBlocksOverridden - ); - - setIsBlocked(isBlocked); - - return isBlocked; - }; - - const handleSubmit = (data) => { - const { - timeZone, - } = intl; - - const requestData = cloneDeep(data); - - const { - requestExpirationDate, - holdShelfExpirationDate, - holdShelfExpirationTime, - fulfillmentPreference, - deliveryAddressTypeId, - pickupServicePointId, - } = requestData; - - if (hasBlocking()) return undefined; - - if (!requestExpirationDate) { - unset(requestData, 'requestExpirationDate'); - } - if (holdShelfExpirationDate) { - // Recombine the values from datepicker and timepicker into a single date/time - const date = moment.tz(holdShelfExpirationDate, timeZone).format('YYYY-MM-DD'); - const time = holdShelfExpirationTime.replace('Z', ''); - const combinedDateTime = moment.tz(`${date} ${time}`, timeZone); - requestData.holdShelfExpirationDate = combinedDateTime.utc().format(); - } else { - unset(requestData, 'holdShelfExpirationDate'); - } - if (fulfillmentPreference === fulfillmentTypeMap.HOLD_SHELF && isString(deliveryAddressTypeId)) { - unset(requestData, 'deliveryAddressTypeId'); - } - if (fulfillmentPreference === fulfillmentTypeMap.DELIVERY && isString(pickupServicePointId)) { - unset(requestData, 'pickupServicePointId'); - } - - if (isPatronBlocksOverridden) { - requestData.requestProcessingParameters = { - overrideBlocks: { - patronBlock: {}, - }, - }; - } - - requestData.instanceId = request?.instanceId || instanceId || selectedInstance?.id; - requestData.requestLevel = request?.requestLevel || getRequestLevelValue(requestData.createTitleLevelRequest); - - if (requestData.requestLevel === REQUEST_LEVEL_TYPES.ITEM) { - requestData.holdingsRecordId = request?.holdingsRecordId || selectedItem?.holdingsRecordId; - } - - if (requestData.requestLevel === REQUEST_LEVEL_TYPES.TITLE) { - unset(requestData, 'itemId'); - unset(requestData, 'holdingsRecordId'); - unset(requestData, RESOURCE_TYPES.ITEM); - } - - unset(requestData, 'itemRequestCount'); - unset(requestData, 'titleRequestCount'); - unset(requestData, 'createTitleLevelRequest'); - unset(requestData, 'numberOfReorderableRequests'); - unset(requestData, RESOURCE_TYPES.INSTANCE); - unset(requestData, 'keyOfItemBarcodeField'); - unset(requestData, 'keyOfUserBarcodeField'); - unset(requestData, 'keyOfInstanceIdField'); - unset(requestData, 'keyOfRequestTypeField'); - - return onSubmit(requestData); - }; - - return ( - - ); -}; - -RequestFormContainer.propTypes = { - request: PropTypes.object, - parentResources: PropTypes.object, - onSubmit: PropTypes.func.isRequired, -}; - -export default RequestFormContainer; diff --git a/src/deprecated/components/RequestFormContainer/RequestFormContainer.test.js b/src/deprecated/components/RequestFormContainer/RequestFormContainer.test.js deleted file mode 100644 index 6c858f75..00000000 --- a/src/deprecated/components/RequestFormContainer/RequestFormContainer.test.js +++ /dev/null @@ -1,388 +0,0 @@ -import { - render, - screen, - fireEvent, -} from '@folio/jest-config-stripes/testing-library/react'; - -import RequestFormContainer from './RequestFormContainer'; -import RequestForm from '../RequestForm/RequestForm'; -import { - REQUEST_LEVEL_TYPES, - fulfillmentTypeMap, -} from '../../../constants'; - -jest.mock('../RequestForm/RequestForm', () => jest.fn(() =>
)); - -const defaultProps = { - parentResources: {}, - request: { - item: {}, - instance: {}, - requester: {}, - requesterId: 'requesterId', - instanceId: 'instanceId', - holdingsRecordId: '', - }, - onSubmit: jest.fn(), -}; -const testIds = { - requestForm: 'requestForm', -}; - -describe('RequestFormContainer', () => { - afterEach(() => { - jest.clearAllMocks(); - }); - - describe('Initial render', () => { - beforeEach(() => { - render( - - ); - }); - - it('should trigger RequestForm with correct props', () => { - const expectedProps = { - parentResources: defaultProps.parentResources, - request: defaultProps.request, - blocked: false, - selectedItem: defaultProps.request.item, - selectedUser: { - ...defaultProps.request.requester, - id: defaultProps.request.requesterId, - }, - selectedInstance: defaultProps.request.instance, - isPatronBlocksOverridden: false, - instanceId: '', - onGetPatronManualBlocks: expect.any(Function), - onGetAutomatedPatronBlocks: expect.any(Function), - onSetBlocked: expect.any(Function), - onSetSelectedItem: expect.any(Function), - onSetSelectedUser: expect.any(Function), - onSetSelectedInstance: expect.any(Function), - onSetIsPatronBlocksOverridden: expect.any(Function), - onSetInstanceId: expect.any(Function), - onSubmit: expect.any(Function), - }; - - expect(RequestForm).toHaveBeenCalledWith(expect.objectContaining(expectedProps), {}); - }); - }); - - describe('Submit handling', () => { - const basicSubmitData = { - requestExpirationDate: null, - fulfillmentPreference: null, - holdShelfExpirationDate: null, - pickupServicePointId: 'pickupServicePointId', - deliveryAddressTypeId: 'deliveryAddressTypeId', - holdingsRecordId: null, - itemRequestCount: null, - titleRequestCount: null, - createTitleLevelRequest: false, - numberOfReorderableRequests: null, - instance: null, - keyOfItemBarcodeField: 0, - keyOfUserBarcodeField: 0, - keyOfInstanceIdField: 0, - keyOfRequestTypeField: 0, - }; - - describe('When item level request', () => { - const requestExpirationDate = new Date().toISOString(); - const submitData = { - ...basicSubmitData, - requestLevel: REQUEST_LEVEL_TYPES.ITEM, - fulfillmentPreference: fulfillmentTypeMap.HOLD_SHELF, - requestExpirationDate, - }; - const props = { - ...defaultProps, - itemId: 'itemId', - item: { - id: 'id', - }, - }; - const selectedItem = { - holdingsRecordId: 'holdingsRecordId', - }; - const selectItemLabel = 'Select Item'; - - beforeEach(() => { - RequestForm.mockImplementation(({ - onSubmit, - onSetSelectedItem, - }) => ( - <> -
onSubmit(submitData)} - /> - - - )); - - render( - - ); - - const selectItemButton = screen.getByText(selectItemLabel); - - fireEvent.click(selectItemButton); - }); - - it('should submit form data', () => { - const expectedArg = { - holdingsRecordId: selectedItem.holdingsRecordId, - fulfillmentPreference: fulfillmentTypeMap.HOLD_SHELF, - instanceId: defaultProps.request.instanceId, - requestLevel: REQUEST_LEVEL_TYPES.ITEM, - pickupServicePointId: submitData.pickupServicePointId, - item: submitData.item, - itemId: submitData.itemId, - requestExpirationDate, - }; - const requestForm = screen.getByTestId(testIds.requestForm); - - fireEvent.submit(requestForm); - - expect(defaultProps.onSubmit).toHaveBeenCalledWith(expectedArg); - }); - }); - - describe('When title level request', () => { - const submitData = { - ...basicSubmitData, - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - fulfillmentPreference: fulfillmentTypeMap.DELIVERY, - createTitleLevelRequest: true, - }; - const props = { - ...defaultProps, - request: { - ...defaultProps.request, - instanceId: null, - }, - }; - const selectedInstance = { - id: 'selectedInstanceId', - }; - const selectInstanceLabel = 'Select Instance'; - - beforeEach(() => { - RequestForm.mockImplementation(({ - onSubmit, - onSetSelectedInstance, - }) => ( - <> - onSubmit(submitData)} - /> - - - )); - - render( - - ); - - const selectInstanceButton = screen.getByText(selectInstanceLabel); - - fireEvent.click(selectInstanceButton); - }); - - it('should submit form data', () => { - const expectedArg = { - fulfillmentPreference: fulfillmentTypeMap.DELIVERY, - instanceId: selectedInstance.id, - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - deliveryAddressTypeId: submitData.deliveryAddressTypeId, - }; - const requestForm = screen.getByTestId(testIds.requestForm); - - fireEvent.submit(requestForm); - - expect(defaultProps.onSubmit).toHaveBeenCalledWith(expectedArg); - }); - }); - - describe('When patron blocks set to overridden', () => { - const submitData = { - ...basicSubmitData, - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - fulfillmentPreference: fulfillmentTypeMap.DELIVERY, - createTitleLevelRequest: true, - }; - const overridePatronBlocksLabel = 'Override patron blocks'; - - beforeEach(() => { - RequestForm.mockImplementation(({ - onSubmit, - onSetIsPatronBlocksOverridden, - }) => ( - <> - onSubmit(submitData)} - /> - - - )); - - render( - - ); - - const overridePatronBlocksButton = screen.getByText(overridePatronBlocksLabel); - - fireEvent.click(overridePatronBlocksButton); - }); - - it('should submit form data', () => { - const expectedArg = { - fulfillmentPreference: fulfillmentTypeMap.DELIVERY, - instanceId: defaultProps.request.instanceId, - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - deliveryAddressTypeId: submitData.deliveryAddressTypeId, - requestProcessingParameters: { - overrideBlocks: { - patronBlock: {}, - }, - }, - }; - const requestForm = screen.getByTestId(testIds.requestForm); - - fireEvent.submit(requestForm); - - expect(defaultProps.onSubmit).toHaveBeenCalledWith(expectedArg); - }); - }); - - describe('When patron has blocks', () => { - const submitData = { - ...basicSubmitData, - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - fulfillmentPreference: fulfillmentTypeMap.DELIVERY, - createTitleLevelRequest: true, - }; - const props = { - ...defaultProps, - parentResources: { - patronBlocks: { - records: [ - { - requests: true, - expirationDate: new Date().toISOString(), - } - ], - }, - automatedPatronBlocks: { - records: [ - { - blockRequests: {}, - message: 'block message', - }, - { - message: 'block message 2', - } - ], - }, - }, - }; - - beforeEach(() => { - RequestForm.mockImplementation(({ - onSubmit, - }) => ( - onSubmit(submitData)} - /> - )); - - render( - - ); - }); - - it('should not submit form data', () => { - const requestForm = screen.getByTestId(testIds.requestForm); - - fireEvent.submit(requestForm); - - expect(defaultProps.onSubmit).not.toHaveBeenCalled(); - }); - }); - - describe('When hold shelf expiration date is presented', () => { - const holdShelfExpirationTime = new Date().toTimeString(); - const submitData = { - ...basicSubmitData, - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - fulfillmentPreference: fulfillmentTypeMap.DELIVERY, - createTitleLevelRequest: true, - holdShelfExpirationDate: new Date().toDateString(), - holdShelfExpirationTime, - }; - - beforeEach(() => { - RequestForm.mockImplementation(({ - onSubmit, - }) => ( - onSubmit(submitData)} - /> - )); - - render( - - ); - }); - - it('should submit form data', () => { - const expectedArg = { - fulfillmentPreference: fulfillmentTypeMap.DELIVERY, - instanceId: defaultProps.request.instanceId, - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - deliveryAddressTypeId: submitData.deliveryAddressTypeId, - holdShelfExpirationTime, - holdShelfExpirationDate: expect.any(String), - }; - const requestForm = screen.getByTestId(testIds.requestForm); - - fireEvent.submit(requestForm); - - expect(defaultProps.onSubmit).toHaveBeenCalledWith(expectedArg); - }); - }); - }); -}); diff --git a/src/deprecated/components/ViewRequest/ViewRequest.js b/src/deprecated/components/ViewRequest/ViewRequest.js deleted file mode 100644 index 326baffd..00000000 --- a/src/deprecated/components/ViewRequest/ViewRequest.js +++ /dev/null @@ -1,865 +0,0 @@ -import { - get, - isEqual, - keyBy, -} from 'lodash'; -import React from 'react'; -import PropTypes from 'prop-types'; -import queryString from 'query-string'; -import { - FormattedMessage, - FormattedDate, - FormattedTime, - injectIntl, -} from 'react-intl'; -import moment from 'moment-timezone'; - -import { - IfPermission, - IntlConsumer, - TitleManager, -} from '@folio/stripes/core'; -import { - Button, - Accordion, - AccordionSet, - AccordionStatus, - Col, - Callout, - Icon, - PaneHeaderIconButton, - KeyValue, - Layer, - Pane, - PaneMenu, - Row, - NoValue, -} from '@folio/stripes/components'; -import { - ViewMetaData, - withTags, - NotesSmartAccordion, -} from '@folio/stripes/smart-components'; - -import ViewRequestShortcutsWrapper from '../../../components/ViewRequestShortcutsWrapper'; -import CancelRequestDialog from '../../../CancelRequestDialog'; -import ItemDetail from '../../../ItemDetail'; -import TitleInformation from '../../../components/TitleInformation'; -import UserDetail from '../../../UserDetail'; -import RequestFormContainer from '../RequestFormContainer/RequestFormContainer'; -import PositionLink from '../../../PositionLink'; -import MoveRequestManager from '../MoveRequestManager/MoveRequestManager'; -import { - requestStatuses, - REQUEST_LEVEL_TYPES, - requestTypesTranslations, - requestStatusesTranslations, - REQUEST_LAYERS, -} from '../../../constants'; -import { - toUserAddress, - isDelivery, - getFullName, - generateUserName, - isValidRequest, - isVirtualItem, - isVirtualPatron, - getRequestErrorMessage, -} from '../../../utils'; -import { getTlrSettings } from '../../utils'; -import urls from '../../../routes/urls'; - -const CREATE_SUCCESS = 'CREATE_SUCCESS'; - -class ViewRequest extends React.Component { - static manifest = { - selectedRequest: { - type: 'okapi', - path: 'circulation/requests/:{id}', - shouldRefresh: (resource, action, refresh) => { - const { - path, - originatingActionType, - } = action.meta; - - if (originatingActionType.includes(CREATE_SUCCESS)) { - return false; - } - - return refresh || (path && path.match(/link/)); - }, - throwErrors: false, - }, - }; - - static propTypes = { - editLink: PropTypes.string, - location: PropTypes.shape({ - pathname: PropTypes.string.isRequired, - search: PropTypes.string, - }).isRequired, - history: PropTypes.shape({ - push: PropTypes.func.isRequired, - }).isRequired, - joinRequest: PropTypes.func.isRequired, - findResource: PropTypes.func.isRequired, - mutator: PropTypes.shape({ - selectedRequest: PropTypes.shape({ - PUT: PropTypes.func, - }), - }).isRequired, - onClose: PropTypes.func.isRequired, - onCloseEdit: PropTypes.func.isRequired, - onEdit: PropTypes.func, - onDuplicate: PropTypes.func, - buildRecordsForHoldsShelfReport: PropTypes.func.isRequired, - optionLists: PropTypes.object, - tagsToggle: PropTypes.func, - paneWidth: PropTypes.string, - patronGroups: PropTypes.arrayOf(PropTypes.object), - parentMutator: PropTypes.object, - parentResources: PropTypes.shape({ - configs: PropTypes.object.isRequired, - }).isRequired, - resources: PropTypes.shape({ - selectedRequest: PropTypes.shape({ - hasLoaded: PropTypes.bool.isRequired, - other: PropTypes.shape({ - totalRecords: PropTypes.number, - }), - records: PropTypes.arrayOf(PropTypes.object), - }), - }), - query: PropTypes.object, - stripes: PropTypes.shape({ - hasPerm: PropTypes.func.isRequired, - connect: PropTypes.func.isRequired, - logger: PropTypes.shape({ - log: PropTypes.func.isRequired, - }).isRequired, - }).isRequired, - intl: PropTypes.object, - tagsEnabled: PropTypes.bool, - match: PropTypes.object, - }; - - static defaultProps = { - editLink: '', - paneWidth: '50%', - onEdit: () => { }, - }; - - constructor(props) { - super(props); - - const { titleLevelRequestsFeatureEnabled } = getTlrSettings(props.parentResources.configs.records[0]?.value); - - this.state = { - request: {}, - moveRequest: false, - titleLevelRequestsFeatureEnabled, - }; - - const { stripes: { connect } } = props; - - this.cViewMetaData = connect(ViewMetaData); - this.connectedCancelRequestDialog = connect(CancelRequestDialog); - this.cancelRequest = this.cancelRequest.bind(this); - this.update = this.update.bind(this); - this.callout = React.createRef(); - this.accordionStatusRef = React.createRef(); - } - - componentDidMount() { - const requests = this.props.resources.selectedRequest; - - this._isMounted = true; - if (requests && requests.hasLoaded) { - this.loadFullRequest(requests.records[0]); - } - } - - // Use componentDidUpdate to pull in metadata from the related user and item records - componentDidUpdate(prevProps) { - const prevRQ = prevProps.resources.selectedRequest; - const currentRQ = this.props.resources.selectedRequest; - const prevSettings = prevProps.parentResources.configs.records[0]?.value; - const currentSettings = this.props.parentResources.configs.records[0]?.value; - - // Only update if actually needed (otherwise, this gets called way too often) - if (prevRQ && currentRQ && currentRQ.hasLoaded) { - if ((!isEqual(prevRQ.records[0], currentRQ.records[0]))) { - this.loadFullRequest(currentRQ.records[0]); - } - } - - if (prevSettings !== currentSettings) { - const { titleLevelRequestsFeatureEnabled } = getTlrSettings(currentSettings); - - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ titleLevelRequestsFeatureEnabled }); - } - } - - componentWillUnmount() { - this._isMounted = false; - } - - loadFullRequest(basicRequest) { - return this.props.joinRequest(basicRequest).then(request => { - if (this._isMounted) { - this.setState({ request }); - } - }); - } - - update(record) { - const requestFromProps = this.getRequestFromProps() || {}; - const updatedRecord = { - ...requestFromProps, - ...record, - }; - - // Remove the "enhanced record" fields that aren't part of the request schema (and thus can't) - // be included in the record PUT, or the save will fail - delete updatedRecord.requesterName; - delete updatedRecord.requesterBarcode; - delete updatedRecord.patronGroup; - delete updatedRecord.itemBarcode; - delete updatedRecord.title; - delete updatedRecord.location; - delete updatedRecord.loan; - delete updatedRecord.itemStatus; - delete updatedRecord.titleRequestCount; - delete updatedRecord.itemRequestCount; - delete updatedRecord.numberOfReorderableRequests; - delete updatedRecord.holdShelfExpirationTime; - - this.props.mutator.selectedRequest.PUT(updatedRecord).then(() => { - this.props.onCloseEdit(); - this.callout.current.sendCallout({ - message: ( - - ), - }); - }).catch(() => { - this.callout.current.sendCallout({ - message: , - type: 'error', - }); - }); - } - - cancelRequest(cancellationInfo) { - const { - resources, - mutator, - onCloseEdit, - buildRecordsForHoldsShelfReport, - intl, - } = this.props; - - // Get the initial request data, mix in the cancellation info, PUT, - // and then close cancel/edit modes since cancelled requests can't be edited. - const request = get(resources, ['selectedRequest', 'records', 0], {}); - const cancelledRequest = { - ...request, - ...cancellationInfo, - }; - - - mutator.selectedRequest.PUT(cancelledRequest) - .catch(resp => { - resp.json() - .then(res => { - res.errors.forEach(error => { - this.callout.current.sendCallout({ - message: getRequestErrorMessage(error, intl), - type: 'error', - }); - }); - }); - }) - .finally(() => { - this.setState({ isCancellingRequest: false }); - onCloseEdit(); - buildRecordsForHoldsShelfReport(); - }); - } - - onMove = async (request) => { - const { - history, - location: { search }, - } = this.props; - const { titleLevelRequestsFeatureEnabled } = this.state; - const id = titleLevelRequestsFeatureEnabled ? request.instanceId : request.itemId; - - this.loadFullRequest(request); - - history.push(`${urls.requestQueueView(request.id, id)}${search}`, { afterMove: true }); - } - - closeMoveRequest = () => { - this.setState({ moveRequest: false }); - } - - openMoveRequest = () => { - this.setState({ moveRequest: true }); - } - - onReorderRequest = (request) => { - const { - location: { search }, - history, - } = this.props; - const { titleLevelRequestsFeatureEnabled } = this.state; - const idForHistory = titleLevelRequestsFeatureEnabled ? request.instanceId : request.itemId; - - history.push(`${urls.requestQueueView(request.id, idForHistory)}${search}`, { request }); - } - - getRequestFromProps = () => { - const { - resources: { - selectedRequest, - }, - match: { - params: { - id, - }, - }, - } = this.props; - const currentRequest = selectedRequest?.records || []; - - if (!id || currentRequest.length === 0) return null; - - return currentRequest.find(r => r.id === id); - } - - getRequest() { - const curRequest = this.getRequestFromProps(); - - if (!curRequest) return null; - - return (curRequest.id === this.state.request.id) ? this.state.request : curRequest; - } - - getPickupServicePointName(request) { - if (!request) return ''; - const { optionLists: { servicePoints } } = this.props; - const servicePoint = servicePoints.find(sp => (sp.id === request.pickupServicePointId)); - - return get(servicePoint, ['name'], ''); - } - - renderLayer(request) { - const { - optionLists, - location, - stripes, - onCloseEdit, - findResource, - patronGroups, - parentMutator, - } = this.props; - const { - titleLevelRequestsFeatureEnabled, - } = this.state; - - const query = location.search ? queryString.parse(location.search) : {}; - - if (query.layer === REQUEST_LAYERS.EDIT) { - // The hold shelf expiration date is stored as a single value (e.g., 20201101T23:59:00-0400), - // but it's exposed in the UI as separate date- and time-picker components. - let momentDate; - if (request.holdShelfExpirationDate) { - momentDate = moment.tz(request.holdShelfExpirationDate, this.props.intl.timeZone); - } else { - momentDate = moment(); - } - - return ( - - {intl => ( - - { this.update(record); }} - onCancel={onCloseEdit} - onCancelRequest={this.cancelRequest} - optionLists={optionLists} - patronGroups={patronGroups} - query={this.props.query} - parentMutator={parentMutator} - findResource={findResource} - isTlrEnabledOnEditPage={titleLevelRequestsFeatureEnabled} - /> - - ) } - - ); - } - - return null; - } - - renderDetailMenu(request) { - const { - tagsEnabled, - tagsToggle, - } = this.props; - - const tags = ((request && request.tags) || {}).tagList || []; - - const requestStatus = get(request, ['status'], '-'); - const closedStatuses = [requestStatuses.CANCELLED, requestStatuses.FILLED, requestStatuses.PICKUP_EXPIRED, requestStatuses.UNFILLED]; - const isRequestClosed = closedStatuses.includes(requestStatus); - - return ( - - { - tagsEnabled && - - {ariaLabel => ( - - )} - - } - - ); - } - - renderRequest(request) { - const { - stripes, - patronGroups, - optionLists: { cancellationReasons }, - } = this.props; - const { - isCancellingRequest, - moveRequest, - titleLevelRequestsFeatureEnabled, - } = this.state; - const { - requestLevel, - item, - requester - } = request; - - const getPickupServicePointName = this.getPickupServicePointName(request); - const requestStatus = get(request, ['status'], '-'); - const isRequestClosed = requestStatus.startsWith('Closed'); - const isRequestNotFilled = requestStatus === requestStatuses.NOT_YET_FILLED; - const isRequestOpen = requestStatus.startsWith('Open'); - const cancellationReasonMap = keyBy(cancellationReasons, 'id'); - const isRequestValid = isValidRequest(request); - const isDCBTransaction = isVirtualPatron(requester?.personal?.lastName) || isVirtualItem(request?.instanceId, request?.holdingsRecordId); - - let deliveryAddressDetail; - let selectedDelivery = false; - - if (isDelivery(request)) { - selectedDelivery = true; - const deliveryAddressType = get(request, 'deliveryAddressTypeId', null); - - if (deliveryAddressType) { - const addresses = get(request, 'requester.personal.addresses', []); - const deliveryLocations = keyBy(addresses, 'addressTypeId'); - deliveryAddressDetail = toUserAddress(deliveryLocations[deliveryAddressType]); - } - } - - const holdShelfExpireDate = get(request, 'holdShelfExpirationDate', '') && - [requestStatuses.AWAITING_PICKUP, requestStatuses.PICKUP_EXPIRED].includes(get(request, ['status'], '')) - ? - : '-'; - - const expirationDate = (get(request, 'requestExpirationDate', '')) - ? - : '-'; - - const showActionMenu = stripes.hasPerm('ui-requests.create') - || stripes.hasPerm('ui-requests.edit') - || stripes.hasPerm('ui-requests.moveRequest.execute') - || stripes.hasPerm('ui-requests.reorderQueue.execute') || !isDCBTransaction; - - const actionMenu = ({ onToggle }) => { - if (isRequestClosed) { - if (!isRequestValid || (requestLevel === REQUEST_LEVEL_TYPES.TITLE && !titleLevelRequestsFeatureEnabled) || isDCBTransaction) { - return null; - } - - return ( - - - - ); - } - - return ( - <> - - { - isRequestValid && !isDCBTransaction && - - } - - - { - isRequestValid && !isDCBTransaction && - - - - } - {item && isRequestNotFilled && isRequestValid && !isDCBTransaction && - - - } - {isRequestOpen && isRequestValid && !isDCBTransaction && - - - } - - ); - }; - - const referredRecordData = { - instanceTitle: request.instance.title, - instanceId: request.instanceId, - itemBarcode: request.item?.barcode, - itemId: request.itemId, - holdingsRecordId: request.holdingsRecordId, - requesterName: getFullName(request.requester), - requesterId: request.requester?.id ?? request.requesterId, - requestCreateDate: request.metadata.createdDate, - }; - - const isDuplicatingDisabled = - isRequestClosed && - request.requestLevel === REQUEST_LEVEL_TYPES.TITLE && - !this.state.titleLevelRequestsFeatureEnabled; - const requestTypeMessageKey = requestTypesTranslations[request.requestType]; - const requestTypeMessage = requestTypeMessageKey ? : ; - const requestStatusMessageKey = requestStatusesTranslations[request.status]; - const requestStatusMessage = requestStatusMessageKey ? : ; - - return ( - } - lastMenu={this.renderDetailMenu(request)} - dismissible - {... (showActionMenu ? { actionMenu } : {})} - onClose={this.props.onClose} - > - this.props.onDuplicate(request)} - onEdit={this.props.onEdit} - accordionStatusRef={this.accordionStatusRef} - isDuplicatingDisabled={isDuplicatingDisabled} - isEditingDisabled={isRequestClosed} - stripes={stripes} - > - - - - - } - > - - - - } - > - { item - ? ( - - ) - : ( - - ) - } - - } - > - - - {request.metadata && } - - - - - } - value={requestTypeMessage} - /> - - - } - value={requestStatusMessage} - /> - - - } - value={expirationDate} - /> - - - } - value={holdShelfExpireDate} - /> - - - - - } - value={ - - } - /> - - - } - value={} - /> - - {request.cancellationReasonId && - - } - value={get(cancellationReasonMap[request.cancellationReasonId], 'name', '-')} - /> - } - {request.cancellationAdditionalInformation && - - } - value={request.cancellationAdditionalInformation} - /> - } - - } - value={request.patronComments} - /> - - - - - {message => message} - - } - > - - - } - pathToNoteCreate="/requests/notes/new" - pathToNoteDetails="/requests/notes" - /> - - - this.setState({ isCancellingRequest: false })} - request={request} - stripes={stripes} - /> - - {moveRequest && - } - - - ); - } - - renderSpinner() { - return ( - } - lastMenu={this.renderDetailMenu()} - dismissible - onClose={this.props.onClose} - > -
- -
-
- ); - } - - render() { - const request = this.getRequest(); - - const content = request - ? this.renderLayer(request) || this.renderRequest(request) - : this.renderSpinner(); - - return ( - <> - {content} - - - ); - } -} - -export default withTags(injectIntl(ViewRequest)); diff --git a/src/deprecated/components/ViewRequest/ViewRequest.test.js b/src/deprecated/components/ViewRequest/ViewRequest.test.js deleted file mode 100644 index 6cec2c27..00000000 --- a/src/deprecated/components/ViewRequest/ViewRequest.test.js +++ /dev/null @@ -1,493 +0,0 @@ -import moment from 'moment-timezone'; - -import { - render, - screen, -} from '@folio/jest-config-stripes/testing-library/react'; - -import { - CommandList, - defaultKeyboardShortcuts, -} from '@folio/stripes/components'; - -import ViewRequest from './ViewRequest'; -import RequestForm from '../RequestForm/RequestForm'; -import { - INVALID_REQUEST_HARDCODED_ID, - requestStatuses, - REQUEST_LEVEL_TYPES, - DCB_INSTANCE_ID, - DCB_HOLDINGS_RECORD_ID, -} from '../../../constants'; -import { - duplicateRecordShortcut, - editRecordShortcut, -} from '../../../../test/jest/helpers'; - -jest.mock('../RequestForm/RequestForm', () => jest.fn(() => null)); -jest.mock('../MoveRequestManager/MoveRequestManager', () => jest.fn(() => null)); -jest.mock('../../../ItemDetail', () => jest.fn(() => null)); -jest.mock('../../../UserDetail', () => jest.fn(() => null)); -jest.mock('../../../CancelRequestDialog', () => jest.fn(() => null)); -jest.mock('../../../PositionLink', () => jest.fn(() => null)); -jest.mock('../../../components/TitleInformation', () => jest.fn(() => null)); - -describe('ViewRequest', () => { - const labelIds = { - duplicateRequest: 'ui-requests.actions.duplicateRequest', - cancelRequest: 'ui-requests.cancel.cancelRequest', - edit: 'ui-requests.actions.edit', - moveRequest: 'ui-requests.actions.moveRequest', - reorderQueue: 'ui-requests.actions.reorderQueue', - requestDetailTitle: 'ui-requests.request.detail.title', - }; - const mockedRequest = { - instance: { - title: 'Title', - }, - item: { - barcode: 'barcode', - }, - id: 'testId', - holdShelfExpirationDate: 'Wed Nov 24 2021 14:38:30', - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - status: requestStatuses.CANCELLED, - pickupServicePointId: 'servicePoint', - metadata: { - createdDate: 'createdDate', - }, - }; - const mockedRequestWithDCBUser = { - ...mockedRequest, - requester: { - personal: { - lastName: 'DcbSystem', - } - } - }; - const mockedRequestWithVirtualItem = { - ...mockedRequest, - instanceId: DCB_INSTANCE_ID, - holdingsRecordId: DCB_HOLDINGS_RECORD_ID, - }; - const mockedLocation = { - pathname: 'pathname', - search: null, - }; - const mockedConfig = { - records: [ - { value: '{"titleLevelRequestsFeatureEnabled":true}' }, - ], - }; - const defaultProps = { - location: mockedLocation, - history: { - push: jest.fn(), - }, - joinRequest: jest.fn(() => new Promise((resolve) => { - resolve({ id: 'id' }); - })), - findResource: jest.fn(), - mutator: {}, - onClose: jest.fn(), - onCloseEdit: jest.fn(), - buildRecordsForHoldsShelfReport: jest.fn(), - optionLists: { - cancellationReasons: [ - { id: '1' }, - { id: '2' }, - ], - servicePoints: [ - { id: 'servicePoint' }, - ], - }, - parentResources: { - configs: mockedConfig, - }, - resources: { - selectedRequest: { - hasLoaded: true, - records: [ - mockedRequest, - ], - }, - }, - stripes: { - hasPerm: jest.fn(() => true), - connect: jest.fn((component) => component), - logger: { - log: jest.fn(), - }, - }, - match: { - params: { - id: 'testId', - }, - }, - }; - const defaultDCBLendingProps = { - ...defaultProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [ - mockedRequestWithDCBUser, - ], - }, - } - }; - const defaultDCBBorrowingProps = { - ...defaultProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [ - mockedRequestWithVirtualItem, - ], - }, - } - }; - const renderViewRequest = (props) => render( - - - - ); - - describe('Non DCB Transactions', () => { - beforeEach(() => { - renderViewRequest(defaultProps); - }); - - afterEach(() => { - RequestForm.mockClear(); - }); - - it('should render request detail title', () => { - expect(screen.getByText(labelIds.requestDetailTitle)).toBeInTheDocument(); - }); - - describe('when work with request editing', () => { - beforeAll(() => { - mockedLocation.search = '?layer=edit'; - }); - - it('should set "createTitleLevelRequest" to false when try to edit existed request', () => { - const expectedResult = { - initialValues : { - requestExpirationDate: null, - holdShelfExpirationDate: mockedRequest.holdShelfExpirationDate, - holdShelfExpirationTime: moment(mockedRequest.holdShelfExpirationDate).format('HH:mm'), - createTitleLevelRequest: false, - ...mockedRequest, - }, - }; - - expect(RequestForm).toHaveBeenCalledWith(expect.objectContaining(expectedResult), {}); - }); - }); - - describe('when not working with request editing', () => { - beforeAll(() => { - mockedLocation.search = null; - }); - - describe('when current request is closed', () => { - describe('request is valid', () => { - describe('TLR in enabled', () => { - beforeAll(() => { - mockedConfig.records[0].value = '{"titleLevelRequestsFeatureEnabled":true}'; - }); - - it('should render "Duplicate" button', () => { - expect(screen.getByText(labelIds.duplicateRequest)).toBeInTheDocument(); - }); - }); - - describe('TLR in disabled', () => { - beforeAll(() => { - mockedConfig.records[0].value = '{"titleLevelRequestsFeatureEnabled":false}'; - }); - - it('should not render "Duplicate" button', () => { - expect(screen.queryByText(labelIds.duplicateRequest)).not.toBeInTheDocument(); - }); - }); - }); - - describe('request is not valid', () => { - const closedInvalidRequest = { - ...mockedRequest, - instanceId: INVALID_REQUEST_HARDCODED_ID, - holdingsRecordId: INVALID_REQUEST_HARDCODED_ID, - }; - const props = { - ...defaultProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [closedInvalidRequest], - }, - }, - }; - - beforeEach(() => { - renderViewRequest(props); - }); - - it('should not render "Duplicate" button', () => { - expect(screen.queryByText(labelIds.duplicateRequest)).not.toBeInTheDocument(); - }); - }); - }); - - describe('when current request is open', () => { - const openValidRequest = { - ...mockedRequest, - status: requestStatuses.NOT_YET_FILLED, - }; - - describe('when request is valid', () => { - const props = { - ...defaultProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [openValidRequest], - }, - }, - }; - - beforeEach(() => { - renderViewRequest(props); - }); - - it('actions menu should show all possible actions', () => { - expect(screen.getByText(labelIds.cancelRequest)).toBeInTheDocument(); - expect(screen.getByText(labelIds.edit)).toBeInTheDocument(); - expect(screen.getByText(labelIds.duplicateRequest)).toBeInTheDocument(); - expect(screen.getByText(labelIds.moveRequest)).toBeInTheDocument(); - expect(screen.getByText(labelIds.reorderQueue)).toBeInTheDocument(); - }); - }); - - describe('when request is invalid', () => { - const props = { - ...defaultProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [ - { - ...openValidRequest, - instanceId: INVALID_REQUEST_HARDCODED_ID, - holdingsRecordId: INVALID_REQUEST_HARDCODED_ID, - }, - ], - }, - }, - }; - - beforeEach(() => { - renderViewRequest(props); - }); - - it('should render action menu with only "Cancel request" button', () => { - expect(screen.getByText(labelIds.cancelRequest)).toBeInTheDocument(); - expect(screen.queryByText(labelIds.edit)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.duplicateRequest)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.moveRequest)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.reorderQueue)).not.toBeInTheDocument(); - }); - }); - }); - }); - - describe('Keyboard shortcuts', () => { - it('should check permission when duplicating', () => { - duplicateRecordShortcut(document.body); - expect(defaultProps.stripes.hasPerm).toHaveBeenCalled(); - }); - - it('should check permission on edit', () => { - editRecordShortcut(document.body); - expect(defaultProps.stripes.hasPerm).toHaveBeenCalled(); - }); - }); - }); - - describe('DCB Transactions', () => { - afterEach(() => { - RequestForm.mockClear(); - }); - - describe('when virtual patron-DCB Lending flow', () => { - describe('when in request detail', () => { - beforeAll(() => { - mockedLocation.search = null; - }); - - describe("when current lending request status starts with 'Closed'", () => { - const closedStatuses = [requestStatuses.FILLED, requestStatuses.CANCELLED, requestStatuses.PICKUP_EXPIRED, requestStatuses.UNFILLED]; - const closedRequests = closedStatuses.map(cStatus => ({ - ...mockedRequestWithDCBUser, - status: cStatus, - })); - const closedRequestsProps = closedRequests.map(cReq => ({ - ...defaultDCBLendingProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [ - { - ...defaultDCBLendingProps.resources.selectedRequest.records, - ...cReq, - }, - ], - }, - } - })); - - closedRequestsProps.forEach(props => { - it(`should not render action menu when request status is ${props?.resources?.selectedRequest?.records[0]?.status}`, () => { - renderViewRequest(props); - expect(screen.queryByRole('button', { name: 'Actions' })).toBeNull(); - }); - }); - }); - - describe('when current lending request is open', () => { - const openValidRequest = { - ...mockedRequestWithDCBUser, - status: requestStatuses.NOT_YET_FILLED, - }; - const props = { - ...defaultDCBLendingProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [ - { - ...defaultDCBLendingProps.resources.selectedRequest.records, - ...openValidRequest, - }, - ], - }, - }, - }; - - beforeEach(() => { - renderViewRequest(props); - }); - - it('should render action menu with only "Cancel request" button', () => { - expect(screen.getByText(labelIds.cancelRequest)).toBeInTheDocument(); - expect(screen.queryByText(labelIds.edit)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.duplicateRequest)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.moveRequest)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.reorderQueue)).not.toBeInTheDocument(); - }); - }); - }); - - describe('Keyboard shortcuts', () => { - beforeEach(() => { - renderViewRequest(defaultDCBLendingProps); - }); - it('should check permission when duplicating', () => { - duplicateRecordShortcut(document.body); - expect(defaultProps.stripes.hasPerm).toHaveBeenCalled(); - }); - - it('should check permission on edit', () => { - editRecordShortcut(document.body); - expect(defaultProps.stripes.hasPerm).toHaveBeenCalled(); - }); - }); - }); - - describe('when virtual item-DCB Borrowing flow', () => { - describe('when in request detail', () => { - beforeAll(() => { - mockedLocation.search = null; - }); - - describe('when current borrowing request status starts with "Closed"', () => { - const closedStatuses = [requestStatuses.FILLED, requestStatuses.CANCELLED, requestStatuses.PICKUP_EXPIRED, requestStatuses.UNFILLED]; - const closedRequests = closedStatuses.map(cStatus => ({ - ...mockedRequestWithDCBUser, - status: cStatus, - })); - const closedRequestsProps = closedRequests.map(cReq => ({ - ...defaultDCBBorrowingProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [ - { - ...defaultDCBBorrowingProps.resources.selectedRequest.records, - ...cReq, - }, - ], - }, - } - })); - - closedRequestsProps.forEach(props => { - it(`should not render action menu when request status is ${props?.resources?.selectedRequest?.records[0]?.status}`, () => { - renderViewRequest(props); - expect(screen.queryByRole('button', { name: 'Actions' })).toBeNull(); - }); - }); - }); - - describe('when current borrowing request is open', () => { - const openValidRequest = { - ...mockedRequestWithDCBUser, - status: requestStatuses.NOT_YET_FILLED, - }; - const props = { - ...defaultDCBBorrowingProps, - resources: { - selectedRequest: { - hasLoaded: true, - records: [ - { - ...defaultDCBBorrowingProps.resources.selectedRequest.records, - ...openValidRequest, - }, - ], - }, - }, - }; - - beforeEach(() => { - renderViewRequest(props); - }); - - it('should render action menu with only "Cancel request" button', () => { - expect(screen.getByText(labelIds.cancelRequest)).toBeInTheDocument(); - expect(screen.queryByText(labelIds.edit)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.duplicateRequest)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.moveRequest)).not.toBeInTheDocument(); - expect(screen.queryByText(labelIds.reorderQueue)).not.toBeInTheDocument(); - }); - }); - }); - - describe('Keyboard shortcuts', () => { - beforeEach(() => { - renderViewRequest(defaultDCBBorrowingProps); - }); - it('should check permission when duplicating', () => { - duplicateRecordShortcut(document.body); - expect(defaultProps.stripes.hasPerm).toHaveBeenCalled(); - }); - - it('should check permission on edit', () => { - editRecordShortcut(document.body); - expect(defaultProps.stripes.hasPerm).toHaveBeenCalled(); - }); - }); - }); - }); -}); diff --git a/src/deprecated/constants.js b/src/deprecated/constants.js deleted file mode 100644 index 74d3a06a..00000000 --- a/src/deprecated/constants.js +++ /dev/null @@ -1,8 +0,0 @@ -export const RESOURCE_TYPES = { - ITEM: 'item', - INSTANCE: 'instance', - USER: 'user', - HOLDING: 'holding', - REQUEST_TYPES: 'requestTypes', -}; - diff --git a/src/deprecated/routes/RequestQueueRoute/RequestQueueRoute.js b/src/deprecated/routes/RequestQueueRoute/RequestQueueRoute.js deleted file mode 100644 index a08561fb..00000000 --- a/src/deprecated/routes/RequestQueueRoute/RequestQueueRoute.js +++ /dev/null @@ -1,270 +0,0 @@ -import React from 'react'; -import PropTypes from 'prop-types'; -import { - get, - keyBy, -} from 'lodash'; -import ReactRouterPropTypes from 'react-router-prop-types'; -import { stripesConnect } from '@folio/stripes/core'; - -import RequestQueueView from '../../../views/RequestQueueView'; -import urls from '../../../routes/urls'; -import { requestStatuses } from '../../../constants'; -import { - isPageRequest, -} from '../../../utils'; -import { getTlrSettings } from '../../utils'; - -class RequestQueueRoute extends React.Component { - static getRequest(props) { - const { - location, - resources, - } = props; - - return get(location, 'state.request') || - get(resources, 'request.records[0]'); - } - - static manifest = { - configs: { - type: 'okapi', - records: 'configs', - path: 'configurations/entries', - params: { - query: '(module==SETTINGS and configName==TLR)', - }, - }, - addressTypes: { - type: 'okapi', - path: 'addresstypes', - records: 'addressTypes', - }, - request: { - type: 'okapi', - path: 'circulation/requests', - records: 'requests', - resourceShouldRefresh: true, - params: (_q, _p, _r, _l, props) => { - const request = RequestQueueRoute.getRequest(props); - - return (!request) ? { query: `id==${props.match.params.requestId}` } : null; - }, - }, - holdings: { - type: 'okapi', - records: 'holdingsRecords', - path: 'holdings-storage/holdings', - params: (_q, _p, _r, _l, props) => { - const request = RequestQueueRoute.getRequest(props); - const holdingsRecordId = get(request, 'holdingsRecordId'); - - return (holdingsRecordId) ? { query: `id==${holdingsRecordId}` } : null; - }, - }, - items: { - type: 'okapi', - records: 'items', - path: 'inventory/items', - params: { - query: 'id==:{itemId}', - }, - }, - requests: { - type: 'okapi', - path: 'circulation/requests', - records: 'requests', - accumulate: true, - fetch: false, - shouldRefresh: () => false, - }, - reorderInstanceQueue: { - type: 'okapi', - POST: { - path: 'circulation/requests/queue/instance/:{id}/reorder', - }, - fetch: false, - clientGeneratePk: false, - throwErrors: false, - }, - reorderItemQueue: { - type: 'okapi', - POST: { - path: 'circulation/requests/queue/item/:{id}/reorder', - }, - fetch: false, - clientGeneratePk: false, - throwErrors: false, - }, - }; - - static propTypes = { - location: ReactRouterPropTypes.location, - resources: PropTypes.shape({ - items: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object), - }), - request: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object), - }), - requests: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object), - }).isRequired, - configs: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object).isRequired, - hasLoaded: PropTypes.bool.isRequired, - }).isRequired, - }), - match: PropTypes.object.isRequired, - mutator: PropTypes.shape({ - reorderInstanceQueue: PropTypes.object.isRequired, - reorderItemQueue: PropTypes.object.isRequired, - requests: PropTypes.object.isRequired, - }), - history: PropTypes.shape({ - push: PropTypes.func.isRequired, - goBack: PropTypes.func.isRequired, - }).isRequired, - }; - - componentDidMount() { - this.setTlrSettings(); - } - - componentDidUpdate(prevProps) { - const { configs: prevConfigs } = prevProps.resources; - const { configs } = this.props.resources; - - if ((prevConfigs.hasLoaded !== configs.hasLoaded && configs.hasLoaded)) { - this.setTlrSettings(); - } - } - - setTlrSettings = () => { - const { configs } = this.props.resources; - const { titleLevelRequestsFeatureEnabled } = getTlrSettings(configs.records[0]?.value); - - this.setState({ titleLevelRequestsFeatureEnabled }, this.getRequests); - } - - getRequests = () => { - const { - mutator: { requests }, - resources: { - configs, - }, - match: { - params, - }, - } = this.props; - const { titleLevelRequestsFeatureEnabled } = this.state; - - if (!configs.hasLoaded) { - return; - } - - const path = `circulation/requests/queue/${titleLevelRequestsFeatureEnabled ? 'instance' : 'item'}/${params.id}`; - - requests.reset(); - requests.GET({ path }); - } - - getRequest = () => { - return RequestQueueRoute.getRequest(this.props); - } - - handleClose = () => { - const { - history, - location, - } = this.props; - - if (get(location, 'state.request')) { - history.goBack(); - } else { - const request = this.getRequest(); - history.push(urls.requestView(request.id)); - } - } - - reorder = (requests) => { - const { - mutator: { - reorderInstanceQueue, - reorderItemQueue, - }, - } = this.props; - const { titleLevelRequestsFeatureEnabled } = this.state; - const reorderedQueue = requests.map(({ id, position: newPosition }) => ({ - id, - newPosition, - })); - - return titleLevelRequestsFeatureEnabled - ? reorderInstanceQueue.POST({ reorderedQueue }) - : reorderItemQueue.POST({ reorderedQueue }); - } - - isLoading = () => { - const { resources } = this.props; - - return get(resources, 'requests.isPending', true); - } - - getRequestsWithDeliveryTypes() { - const { resources } = this.props; - const requests = get(resources, 'requests.records', []); - const addressTypes = get(resources, 'addressTypes.records', []); - const addressTypeMap = keyBy(addressTypes, 'id'); - - return requests.map(r => ({ - ...r, - deliveryType: get(addressTypeMap[r.deliveryAddressTypeId], 'addressType'), - })); - } - - render() { - const titleLevelRequestsFeatureEnabled = !!this.state?.titleLevelRequestsFeatureEnabled; - const { resources, location } = this.props; - const request = this.getRequest(); - const requests = this.getRequestsWithDeliveryTypes(); - const notYetFilledRequests = []; - let inProgressRequests = []; - const pageRequests = []; - - requests - .forEach((r) => { - if (isPageRequest(r)) { - pageRequests.push(r); - } else if (r.status === requestStatuses.NOT_YET_FILLED) { - notYetFilledRequests.push(r); - } else { - inProgressRequests.push(r); - } - }); - - inProgressRequests = [ - ...inProgressRequests, - ...pageRequests, // page requests should be shown at the bottom of the accordion - ]; - - return ( - - ); - } -} - -export default stripesConnect(RequestQueueRoute); diff --git a/src/deprecated/routes/RequestQueueRoute/RequestQueueRoute.test.js b/src/deprecated/routes/RequestQueueRoute/RequestQueueRoute.test.js deleted file mode 100644 index 68132f77..00000000 --- a/src/deprecated/routes/RequestQueueRoute/RequestQueueRoute.test.js +++ /dev/null @@ -1,188 +0,0 @@ -import { render } from '@folio/jest-config-stripes/testing-library/react'; -import userEvent from '@folio/jest-config-stripes/testing-library/user-event'; - -import RequestQueueRoute from './RequestQueueRoute'; - -jest.mock('react-router-prop-types', () => ({ - location: jest.fn(), -})); -jest.mock('../../../views/RequestQueueView', () => { - return function MockRequestQueueView({ onClose }) { - return ( -
-

Mock RequestQueueView

- -
- ); - }; -}); -jest.mock('../../../utils', () => ({ - isPageRequest: jest.fn(), -})); -jest.mock('../../utils', () => ({ - getTlrSettings: jest.fn(() => ({ titleLevelRequestsFeatureEnabled: false })), -})); - -const baseMockResources = { - configs: { - records: [ - { - value: {}, - }, - ], - hasLoaded: true, - }, - request: { - records: [ - { - id: '1', - }, - ], - }, - requests: { - records: [ - { - id: '2', - }, - ], - }, -}; -const mockResources = { - request: { - records: [{ - id: '1', - holdingsRecordId: '2', - itemId: '3', - }], - }, - holdings: { - records: [{ - id: '123', - callNumber: 'ABC123', - }] - }, - items: { - records: [{ - id: '3', - }], - }, - configs: { - records: [{ - value: { - titleLevelRequestsFeatureEnabled: true, - }, - }], - hasLoaded: true, - }, - requests: { - records: [{ - shouldRefresh: false, - }], - }, -}; -const baseMockMutator = { - reorderInstanceQueue: {}, - reorderItemQueue: {}, - requests: { - reset: jest.fn(), - GET: jest.fn(), - }, -}; -const mockMutator = { - ...baseMockMutator, - reorderInstanceQueue: { - POST: jest.fn(), - }, - reorderItemQueue: { - POST: jest.fn(), - }, -}; -const mockHistory = { - push: jest.fn(), - goBack: jest.fn(), -}; -const mockMatch = { - params: { - id: '2', - requestId: '1', - }, -}; -const mockLocation = { - state: { - request: { - id: '1', - }, - }, -}; - -describe('RequestQueueRoute', () => { - beforeEach(() => { - jest.clearAllMocks(); - }); - - it('should call requests.GET when component rerender', () => { - const { rerender } = render( - - ); - - rerender( - - ); - - expect(mockMutator.requests.GET).toHaveBeenCalled(); - }); - - it('should call "history.goBack" if "location.state.request" is truthy', async () => { - const goBack = jest.fn(); - const { getByText } = render( - , - ); - - await userEvent.click(getByText('Close')); - - expect(goBack).toHaveBeenCalledTimes(1); - }); - - it('should call "history.push" with the correct URL if "location.state.request" is falsy', async () => { - const push = jest.fn(); - const { getByText } = render( - , - ); - - await userEvent.click(getByText('Close')); - - expect(push).toHaveBeenCalledWith('/requests/view/1'); - }); -}); diff --git a/src/deprecated/routes/RequestsRoute/RequestsRoute.js b/src/deprecated/routes/RequestsRoute/RequestsRoute.js deleted file mode 100644 index 8f25e6f3..00000000 --- a/src/deprecated/routes/RequestsRoute/RequestsRoute.js +++ /dev/null @@ -1,1720 +0,0 @@ -import { - get, - isEmpty, - isArray, - size, -} from 'lodash'; -import React from 'react'; -import PropTypes from 'prop-types'; -import { - stringify, - parse, -} from 'query-string'; -import moment from 'moment-timezone'; -import { - FormattedMessage, - injectIntl, -} from 'react-intl'; -import DOMPurify from 'dompurify'; - -import { - AppIcon, - stripesConnect, - IfPermission, - CalloutContext, - TitleManager, -} from '@folio/stripes/core'; -import { - Button, - Checkbox, - filters2cql, - FormattedTime, - MenuSection, - TextLink, - DefaultMCLRowFormatter, - NoValue, - MCLPagingTypes, -} from '@folio/stripes/components'; -import { - deparseFilters, - makeQueryFunction, - SearchAndSort, -} from '@folio/stripes/smart-components'; -import { - exportCsv, - effectiveCallNumber, - getHeaderWithCredentials, -} from '@folio/stripes/util'; - -import ViewRequest from '../../components/ViewRequest/ViewRequest'; -import RequestFormContainer from '../../components/RequestFormContainer/RequestFormContainer'; - -import { - reportHeaders, - fulfillmentTypes, - expiredHoldsReportHeaders, - SLIPS_TYPE, - createModes, - requestStatusesTranslations, - requestTypesTranslations, - REQUEST_LEVEL_TYPES, - DEFAULT_DISPLAYED_YEARS_AMOUNT, - MAX_RECORDS, - OPEN_REQUESTS_STATUSES, - fulfillmentTypeMap, - DEFAULT_REQUEST_TYPE_VALUE, - INPUT_REQUEST_SEARCH_SELECTOR, - PRINT_DETAILS_COLUMNS, - requestFilterTypes, -} from '../../../constants'; -import { - buildUrl, - getFullName, - duplicateRequest, - convertToSlipData, - getInstanceQueryString, - isDuplicateMode, - generateUserName, - getRequestErrorMessage, - getSelectedSlipDataMulti, - selectedRowsNonPrintable, - getNextSelectedRowsState, -} from '../../../utils'; -import { getTlrSettings } from '../../utils'; -import packageInfo from '../../../../package'; -import CheckboxColumn from '../../../components/CheckboxColumn'; - -import { - PrintButton, - PrintContent, - ErrorModal, - LoadingButton, -} from '../../../components'; - -import { - RequestsFilters, - RequestsFiltersConfig, -} from '../../../components/RequestsFilters'; -import RequestsRouteShortcutsWrapper from '../../../components/RequestsRouteShortcutsWrapper'; -import { - isReorderableRequest, - getFormattedYears, - getStatusQuery, - getFullNameForCsvRecords, - updateQuerySortString, -} from '../../../routes/utils'; -import SinglePrintButtonForPickSlip from '../../../components/SinglePrintButtonForPickSlip'; - -const INITIAL_RESULT_COUNT = 30; -const RESULT_COUNT_INCREMENT = 30; -export const DEFAULT_FORMATTER_VALUE = ''; - -export const getPrintHoldRequestsEnabled = (printHoldRequests) => { - const value = printHoldRequests.records[0]?.value; - const { - printHoldRequestsEnabled = false, - } = value ? JSON.parse(value) : {}; - - return printHoldRequestsEnabled; -}; - -export const getFilteredColumnHeadersMap = (columnHeaders) => ( - columnHeaders.filter(column => column.value !== PRINT_DETAILS_COLUMNS.COPIES && - column.value !== PRINT_DETAILS_COLUMNS.PRINTED) -); - -export const extractPickSlipRequestIds = (pickSlipsData) => { - return pickSlipsData.map(pickSlip => pickSlip['request.requestID']); -}; - -export const getLastPrintedDetails = (printDetails, intl) => { - const fullName = getFullName(printDetails?.lastPrintRequester); - const formattedDate = intl.formatDate(printDetails?.printEventDate); - const formattedTime = intl.formatTime(printDetails?.printEventDate); - const localizedDateTime = `${formattedDate}${formattedTime ? ', ' : ''}${formattedTime}`; - - return fullName + ' ' + localizedDateTime; -}; - -export const urls = { - user: (value, idType) => { - const query = stringify({ query: `(${idType}=="${value}")` }); - return `users?${query}`; - }, - item: (value, idType) => { - let query; - - if (isArray(value)) { - query = `(${value.map((valueItem) => `${idType}=="${valueItem}"`).join(' or ')})`; - } else { - query = `(${idType}=="${value}")`; - } - - query = stringify({ query }); - return `inventory/items?${query}`; - }, - instance: (value) => { - const query = stringify({ query: getInstanceQueryString(value) }); - - return `inventory/instances?${query}`; - }, - loan: (value) => { - const query = stringify({ query: `(itemId=="${value}") and status.name==Open` }); - - return `circulation/loans?${query}`; - }, - requestsForItem: (value) => { - const statusQuery = getStatusQuery(OPEN_REQUESTS_STATUSES); - const query = stringify({ - query: `(itemId=="${value}" and (${statusQuery}))`, - limit: MAX_RECORDS, - }); - - return `circulation/requests?${query}`; - }, - requestsForInstance: (value) => { - const statusQuery = getStatusQuery(OPEN_REQUESTS_STATUSES); - const query = stringify({ - query: `(instanceId=="${value}" and (${statusQuery}))`, - limit: MAX_RECORDS, - }); - - return `circulation/requests?${query}`; - }, - requestPreferences: (value) => { - const query = stringify({ query: `(userId=="${value}")` }); - - return `request-preference-storage/request-preference?${query}`; - }, - holding: (value, idType) => { - const query = stringify({ query: `(${idType}=="${value}")` }); - - return `holdings-storage/holdings?${query}`; - }, - requestTypes: ({ - requesterId, - itemId, - instanceId, - requestId, - operation, - }) => { - if (requestId) { - return `circulation/requests/allowed-service-points?operation=${operation}&requestId=${requestId}`; - } - - let requestUrl = `circulation/requests/allowed-service-points?requesterId=${requesterId}&operation=${operation}`; - - if (itemId) { - requestUrl = `${requestUrl}&itemId=${itemId}`; - } else if (instanceId) { - requestUrl = `${requestUrl}&instanceId=${instanceId}`; - } - - return requestUrl; - }, -}; - -export const getListFormatter = ( - { - getRowURL, - setURL, - }, - { - intl, - selectedRows, - pickSlipsToCheck, - pickSlipsData, - isViewPrintDetailsEnabled, - getPrintContentRef, - pickSlipsPrintTemplate, - toggleRowSelection, - onBeforeGetContentForSinglePrintButton, - onBeforePrintForSinglePrintButton, - onAfterPrintForSinglePrintButton, - } -) => ({ - 'select': rq => ( - ), - 'itemBarcode': rq => (rq.item ? rq.item.barcode : DEFAULT_FORMATTER_VALUE), - 'position': rq => (rq.position || DEFAULT_FORMATTER_VALUE), - 'proxy': rq => (rq.proxy ? getFullName(rq.proxy) : DEFAULT_FORMATTER_VALUE), - 'requestDate': rq => ( - - - - ), - 'requester': rq => (rq.requester ? getFullName(rq.requester) : DEFAULT_FORMATTER_VALUE), - 'singlePrint': rq => { - const singlePrintButtonProps = { - request: rq, - pickSlipsToCheck, - pickSlipsPrintTemplate, - onBeforeGetContentForSinglePrintButton, - pickSlipsData, - getPrintContentRef, - ...(isViewPrintDetailsEnabled && { - onBeforePrintForSinglePrintButton, - onAfterPrintForSinglePrintButton, - }), - }; - return ( - ); - }, - 'requesterBarcode': rq => (rq.requester ? rq.requester.barcode : DEFAULT_FORMATTER_VALUE), - 'requestStatus': rq => (requestStatusesTranslations[rq.status] - ? - : ), - 'type': rq => , - 'title': rq => setURL(rq.id)}>{(rq.instance ? rq.instance.title : DEFAULT_FORMATTER_VALUE)}, - 'year': rq => getFormattedYears(rq.instance?.publication, DEFAULT_DISPLAYED_YEARS_AMOUNT), - 'callNumber': rq => effectiveCallNumber(rq.item), - 'servicePoint': rq => get(rq, 'pickupServicePoint.name', DEFAULT_FORMATTER_VALUE), - 'copies': rq => get(rq, PRINT_DETAILS_COLUMNS.COPIES, DEFAULT_FORMATTER_VALUE), - 'printed': rq => (rq.printDetails ? getLastPrintedDetails(rq.printDetails, intl) : DEFAULT_FORMATTER_VALUE), -}); - -export const buildHoldRecords = (records) => { - return records.map(record => { - if (record.requester) { - const { - firstName, - lastName, - } = record.requester; - - record.requester.name = [lastName, firstName].filter(namePart => namePart).join(', '); - } - - return record; - }); -}; - -class RequestsRoute extends React.Component { - static contextType = CalloutContext; - - static manifest = { - addressTypes: { - type: 'okapi', - path: 'addresstypes', - records: 'addressTypes', - params: { - query: 'cql.allRecords=1 sortby addressType', - limit: MAX_RECORDS, - }, - }, - query: { - initialValue: { sort: 'requestDate' }, - }, - resultCount: { initialValue: INITIAL_RESULT_COUNT }, - resultOffset: { initialValue: 0 }, - records: { - type: 'okapi', - path: 'circulation/requests', - records: 'requests', - resultOffset: '%{resultOffset}', - resultDensity: 'sparse', - perRequest: 100, - throwErrors: false, - GET: { - params: { - query: makeQueryFunction( - 'cql.allRecords=1', - '(requesterId=="%{query.query}" or requester.barcode="%{query.query}*" or instance.title="%{query.query}*" or instanceId="%{query.query}*" or item.barcode=="%{query.query}*" or itemId=="%{query.query}" or itemIsbn="%{query.query}" or searchIndex.callNumberComponents.callNumber=="%{query.query}*" or fullCallNumberIndex=="%{query.query}*")', - { - 'title': 'instance.title', - 'instanceId': 'instanceId', - 'publication': 'instance.publication', - 'itemBarcode': 'item.barcode', - 'callNumber': 'searchIndex.shelvingOrder', - 'type': 'requestType', - 'requester': 'requester.lastName requester.firstName', - 'requestStatus': 'status', - 'servicePoint': 'searchIndex.pickupServicePointName', - 'requesterBarcode': 'requester.barcode', - 'requestDate': 'requestDate', - 'position': 'position/number', - 'proxy': 'proxy', - 'copies': 'printDetails.printCount/number', - 'printed': 'printDetails.printEventDate', - }, - RequestsFiltersConfig, - 2, // do not fetch unless we have a query or a filter - ), - }, - staticFallback: { params: {} }, - }, - }, - reportRecords: { - type: 'okapi', - path: 'circulation/requests', - records: 'requests', - perRequest: 1000, - throwErrors: false, - accumulate: true, - }, - patronGroups: { - type: 'okapi', - path: 'groups', - params: { - query: 'cql.allRecords=1 sortby group', - limit: MAX_RECORDS, - }, - records: 'usergroups', - }, - servicePoints: { - type: 'okapi', - records: 'servicepoints', - path: 'service-points', - params: { - query: 'query=(pickupLocation==true) sortby name', - limit: MAX_RECORDS, - }, - }, - itemUniquenessValidator: { - type: 'okapi', - records: 'items', - accumulate: 'true', - path: 'inventory/items', - fetch: false, - }, - userUniquenessValidator: { - type: 'okapi', - records: 'users', - accumulate: 'true', - path: 'users', - fetch: false, - }, - instanceUniquenessValidator: { - type: 'okapi', - records: 'instances', - accumulate: true, - path: 'inventory/instances', - fetch: false, - }, - patronBlocks: { - type: 'okapi', - records: 'manualblocks', - path: 'manualblocks?query=userId==%{activeRecord.patronId}', - DELETE: { - path: 'manualblocks/%{activeRecord.blockId}', - }, - }, - automatedPatronBlocks: { - type: 'okapi', - records: 'automatedPatronBlocks', - path: 'automated-patron-blocks/%{activeRecord.patronId}', - }, - activeRecord: {}, - expiredHolds: { - accumulate: 'true', - type: 'okapi', - path: 'circulation/requests-report/expired-holds', - fetch: false, - }, - cancellationReasons: { - type: 'okapi', - path: 'cancellation-reason-storage/cancellation-reasons', - records: 'cancellationReasons', - params: { - query: 'cql.allRecords=1 sortby name', - limit: MAX_RECORDS, - }, - }, - staffSlips: { - type: 'okapi', - records: 'staffSlips', - path: 'staff-slips-storage/staff-slips', - params: { - query: 'cql.allRecords=1 sortby name', - limit: MAX_RECORDS, - }, - - throwErrors: false, - }, - pickSlips: { - type: 'okapi', - records: 'pickSlips', - path: 'circulation/pick-slips/%{currentServicePoint.id}', - throwErrors: false, - }, - searchSlips: { - type: 'okapi', - records: 'searchSlips', - path: 'circulation/search-slips/%{currentServicePoint.id}', - throwErrors: false, - }, - printHoldRequests: { - type: 'okapi', - records: 'configs', - path: 'configurations/entries', - params: { - query: '(module==SETTINGS and configName==PRINT_HOLD_REQUESTS)', - }, - }, - currentServicePoint: {}, - tags: { - throwErrors: false, - type: 'okapi', - path: 'tags', - params: { - query: 'cql.allRecords=1 sortby label', - limit: MAX_RECORDS, - }, - records: 'tags', - }, - proxy: { - type: 'okapi', - records: 'proxiesFor', - path: 'proxiesfor', - accumulate: true, - fetch: false, - }, - configs: { - type: 'okapi', - records: 'configs', - path: 'configurations/entries', - params: { - query: '(module==SETTINGS and configName==TLR)', - }, - }, - circulationSettings: { - throwErrors: false, - type: 'okapi', - records: 'circulationSettings', - path: 'circulation/settings', - params: { - query: '(name=printEventLogFeature)', - }, - }, - savePrintDetails: { - type: 'okapi', - POST: { - path: 'circulation/print-events-entry', - }, - fetch: false, - clientGeneratePk: false, - throwErrors: false, - }, - }; - - static propTypes = { - intl: PropTypes.object, - mutator: PropTypes.shape({ - records: PropTypes.shape({ - GET: PropTypes.func, - POST: PropTypes.func, - }), - reportRecords: PropTypes.shape({ - GET: PropTypes.func, - }), - query: PropTypes.object, - requestCount: PropTypes.shape({ - replace: PropTypes.func, - }), - resultOffset: PropTypes.shape({ - replace: PropTypes.func, - }), - resultCount: PropTypes.shape({ - replace: PropTypes.func, - }), - activeRecord: PropTypes.shape({ - update: PropTypes.func, - }), - expiredHolds: PropTypes.shape({ - GET: PropTypes.func, - reset: PropTypes.func, - }), - patronBlocks: PropTypes.shape({ - DELETE: PropTypes.func, - }), - staffSlips: PropTypes.shape({ - GET: PropTypes.func, - }), - pickSlips: PropTypes.shape({ - GET: PropTypes.func, - }).isRequired, - searchSlips: PropTypes.shape({ - GET: PropTypes.func, - }).isRequired, - currentServicePoint: PropTypes.shape({ - update: PropTypes.func.isRequired, - }).isRequired, - proxy: PropTypes.shape({ - reset: PropTypes.func.isRequired, - GET: PropTypes.func.isRequired, - }).isRequired, - circulationSettings: PropTypes.shape({ - GET: PropTypes.func, - }), - savePrintDetails: PropTypes.shape({ - POST: PropTypes.func, - }), - }).isRequired, - resources: PropTypes.shape({ - addressTypes: PropTypes.shape({ - hasLoaded: PropTypes.bool.isRequired, - records: PropTypes.arrayOf(PropTypes.object), - }), - currentServicePoint: PropTypes.object.isRequired, - query: PropTypes.object, - records: PropTypes.shape({ - hasLoaded: PropTypes.bool.isRequired, - isPending: PropTypes.bool.isRequired, - other: PropTypes.shape({ - totalRecords: PropTypes.number, - }), - records: PropTypes.arrayOf(PropTypes.object), - }), - staffSlips: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object).isRequired, - }), - pickSlips: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object).isRequired, - isPending: PropTypes.bool, - }), - searchSlips: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object).isRequired, - isPending: PropTypes.bool, - }), - configs: PropTypes.shape({ - hasLoaded: PropTypes.bool.isRequired, - records: PropTypes.arrayOf(PropTypes.object).isRequired, - }), - printHoldRequests: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object).isRequired, - }), - circulationSettings: PropTypes.shape({ - records: PropTypes.arrayOf(PropTypes.object), - }), - }).isRequired, - stripes: PropTypes.shape({ - connect: PropTypes.func.isRequired, - logger: PropTypes.shape({ - log: PropTypes.func.isRequired, - }).isRequired, - okapi: PropTypes.shape({ - url: PropTypes.string.isRequired, - tenant: PropTypes.string.isRequired, - }), - store: PropTypes.shape({ - getState: PropTypes.func.isRequired, - }), - user: PropTypes.object.isRequired, - timezone: PropTypes.string.isRequired, - locale: PropTypes.string.isRequired, - }).isRequired, - history: PropTypes.object, - location: PropTypes.shape({ - search: PropTypes.string, - pathname: PropTypes.string, - }).isRequired, - match: PropTypes.object.isRequired, - }; - - constructor(props) { - super(props); - - const { - titleLevelRequestsFeatureEnabled = false, - createTitleLevelRequestsByDefault = false, - } = getTlrSettings(props.resources.configs.records[0]?.value); - - this.okapiUrl = props.stripes.okapi.url; - - this.httpHeadersOptions = { - ...getHeaderWithCredentials({ - tenant: this.props.stripes.okapi.tenant, - token: this.props.stripes.store.getState().okapi.token, - }) - }; - - this.getRowURL = this.getRowURL.bind(this); - this.addRequestFields = this.addRequestFields.bind(this); - this.processError = this.processError.bind(this); - this.create = this.create.bind(this); - this.findResource = this.findResource.bind(this); - this.toggleModal = this.toggleModal.bind(this); - this.buildRecords = this.buildRecords.bind(this); - // Map to pass into exportCsv - this.columnHeadersMap = this.getColumnHeaders(reportHeaders); - this.expiredHoldsReportColumnHeaders = this.getColumnHeaders(expiredHoldsReportHeaders); - - this.state = { - csvReportPending: false, - submitting: false, - errorMessage: '', - errorModalData: {}, - servicePointId: '', - requests: [], - selectedId: '', - selectedRows: {}, - titleLevelRequestsFeatureEnabled, - createTitleLevelRequestsByDefault, - isViewPrintDetailsEnabled: false, - }; - - this.pickSlipsPrintContentRef = React.createRef(); - this.searchSlipsPrintContentRef = React.createRef(); - this.paneTitleRef = React.createRef(); - this.printSelectedContentRef = React.createRef(); - } - - static getDerivedStateFromProps(props, state) { - const layer = (props.resources.query || {}).layer; - const newState = {}; - const currViewPrintDetailsSettings = get(props.resources, 'circulationSettings.records[0].value.enablePrintLog') === 'true'; - - if (!layer) { - newState.dupRequest = null; - } - - if (currViewPrintDetailsSettings !== state.isViewPrintDetailsEnabled) { - // Update the `isViewPrintDetailsEnabled` state based on user navigation back to Request App. - newState.isViewPrintDetailsEnabled = currViewPrintDetailsSettings; - } - - return Object.keys(newState).length ? newState : null; - } - - componentDidMount() { - this.setCurrentServicePointId(); - } - - componentDidUpdate(prevProps) { - const patronBlocks = get(this.props.resources, ['patronBlocks', 'records'], []); - const prevBlocks = get(prevProps.resources, ['patronBlocks', 'records'], []); - const { submitting, isViewPrintDetailsEnabled } = this.state; - const prevExpired = prevBlocks.filter(p => moment(moment(p.expirationDate).format()).isSameOrBefore(moment().format()) && p.expirationDate) || []; - const expired = patronBlocks.filter(p => moment(moment(p.expirationDate).format()).isSameOrBefore(moment().format()) && p.expirationDate) || []; - const { id: currentServicePointId } = this.getCurrentServicePointInfo(); - const prevStateServicePointId = get(prevProps.resources.currentServicePoint, 'id'); - const { configs: prevConfigs } = prevProps.resources; - const { resources, location, mutator } = this.props; - const { configs, query } = resources; - const instanceId = parse(location?.search)?.instanceId; - - if (prevExpired.length > 0 && expired.length === 0) { - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ submitting: false }); - } - - if (expired.length > 0 && !submitting) { - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ submitting: true }); - expired.forEach(p => { - mutator.activeRecord.update({ blockId: p.id }); - mutator.patronBlocks.DELETE({ id: p.id }); - }); - } - - if (prevStateServicePointId !== currentServicePointId) { - this.setCurrentServicePointId(); - } - - if (configs?.records[0]?.value !== prevConfigs?.records[0]?.value) { - const { - titleLevelRequestsFeatureEnabled = false, - createTitleLevelRequestsByDefault = false, - } = getTlrSettings(configs.records[0]?.value); - - // eslint-disable-next-line react/no-did-update-set-state - this.setState({ - titleLevelRequestsFeatureEnabled, - createTitleLevelRequestsByDefault, - }); - } - - if (!query.instanceId && instanceId) { - mutator.query.update({ instanceId }); - } - - if (!resources.records.isPending) { - this.onSearchComplete(resources.records); - } - - if (!isViewPrintDetailsEnabled) { - this.handlePrintDetailsDisabled(); - } - } - - handlePrintDetailsDisabled() { - /** - * The function handles the following actions when `isViewPrintDetailsEnabled` is false: - * - * 1. If `filters` in query includes 'PRINT STATUS' filter: - * - it clears the 'PRINT STATUS' filter from query by invoking `handleFilterChange`. - * - * 2. If `sort` in query includes sort by 'printed' or 'copies' column: - * - it removes sorting by 'printed' or 'copies' from the sort query. If both are being sorted, - * the sorting is updated to 'requestDate' instead. - */ - const { resources: { query }, mutator } = this.props; - const printStatusFilterInQuery = this.getActiveFilters()[requestFilterTypes.PRINT_STATUS]; - - if (printStatusFilterInQuery?.length) { - this.handleFilterChange({ name: requestFilterTypes.PRINT_STATUS, values: [] }); - } - - if (query.sort?.includes('printed') || query.sort?.includes('copies')) { - const sort = updateQuerySortString(query.sort); - mutator.query.update({ sort }); - } - } - - toggleAllRows = () => { - const { resources } = this.props; - const { selectedRows } = this.state; - const toggledRows = resources.records.records.reduce((acc, row) => ( - { - ...acc, - [row.id]: row, - } - ), {}); - const filterSelectedRows = rows => { - Object.keys(toggledRows).forEach(id => { - if (rows[id]) delete rows[id]; - }); - return rows; - }; - - this.setState(({ selectedRows: this.getIsAllRowsSelected() ? filterSelectedRows(selectedRows) : { ...selectedRows, ...toggledRows } })); - }; - - getIsAllRowsSelected = () => { - const { resources } = this.props; - const { selectedRows } = this.state; - - if (resources.records.records.length !== 0) { - return resources.records.records.every(({ id }) => Object.keys(selectedRows).includes(id)); - } else { - return false; - } - }; - - onSearchComplete(records) { - const paneTitleRef = this.paneTitleRef.current; - const resultsCount = get(records, 'other.totalRecords', 0); - - if (!!resultsCount && paneTitleRef) { - paneTitleRef.focus(); - } else { - const searchFieldRef = document.getElementById(INPUT_REQUEST_SEARCH_SELECTOR); - - if (searchFieldRef) { - searchFieldRef.focus(); - } - } - } - - async fetchReportData(mutator, query) { - const { GET, reset } = mutator; - - const limit = 1000; - const data = []; - let offset = 0; - let hasData = true; - - while (hasData) { - try { - reset(); - // eslint-disable-next-line no-await-in-loop - const result = await GET({ params: { query, limit, offset } }); - hasData = result.length; - offset += limit; - if (hasData) { - data.push(...result); - } - } catch (err) { - hasData = false; - } - } - - return data; - } - - // Export function for the CSV search report action - async exportData() { - this.setState({ csvReportPending: true }); - - // Build a custom query for the CSV record export, which has to include - // all search and filter parameters - const queryClauses = []; - let queryString; - - const queryTerm = this.props.resources?.query?.query; - const filterQuery = filters2cql(RequestsFiltersConfig, deparseFilters(this.getActiveFilters())); - - if (queryTerm) { - queryString = `(requesterId=="${queryTerm}" or requester.barcode="${queryTerm}*" or item.title="${queryTerm}*" or item.barcode=="${queryTerm}*" or itemId=="${queryTerm}")`; - queryClauses.push(queryString); - } - if (filterQuery) queryClauses.push(filterQuery); - - queryString = queryClauses.join(' and '); - const records = await this.fetchReportData(this.props.mutator.reportRecords, queryString); - const recordsToCSV = this.buildRecords(records); - - this.columnHeadersMap = this.state.isViewPrintDetailsEnabled ? this.columnHeadersMap : - getFilteredColumnHeadersMap(this.columnHeadersMap); - - exportCsv(recordsToCSV, { - onlyFields: this.columnHeadersMap, - excludeFields: ['id'], - }); - - this.setState({ csvReportPending: false }); - } - - getCurrentServicePointInfo = () => { - const { stripes } = this.props; - - const currentState = stripes.store.getState(); - const id = get(currentState, 'okapi.currentUser.curServicePoint.id'); - const name = get(currentState, 'okapi.currentUser.curServicePoint.name'); - - return { id, name }; - }; - - setCurrentServicePointId = () => { - const { - mutator, - resources, - } = this.props; - const { id } = this.getCurrentServicePointInfo(); - - if (resources.currentServicePoint?.id !== id) { - mutator.currentServicePoint.update({ id }); - } - - this.buildRecordsForHoldsShelfReport(); - }; - - getColumnHeaders = (headers) => { - const { intl: { formatMessage } } = this.props; - - return headers.map(item => ({ - label: formatMessage({ id: `ui-requests.${item}` }), - value: item, - })); - }; - - buildRecords(recordsLoaded) { - const result = JSON.parse(JSON.stringify(recordsLoaded)); // Do not mutate the actual resource - const { formatDate, formatTime } = this.props.intl; - - result.forEach(record => { - const contributorNamesMap = []; - const tagListMap = []; - - if (record.instance.contributorNames && record.instance.contributorNames.length > 0) { - record.instance.contributorNames.forEach(item => { - contributorNamesMap.push(item.name); - }); - } - if (record.tags && record.tags.tagList.length > 0) { - record.tags.tagList.forEach(item => { - tagListMap.push(item); - }); - } - if (record.requester) { - record.requester.name = getFullNameForCsvRecords(record.requester); - } - if (record.printDetails) { - const fullName = getFullNameForCsvRecords(record.printDetails.lastPrintRequester); - const lastPrintedDate = record.printDetails.printEventDate || ''; - const date = lastPrintedDate ? `, ${lastPrintedDate}` : ''; - - record.printDetails.lastPrintedDetails = `${fullName}${date}`; - } - if (record.loan) { - const { dueDate } = record.loan; - record.loan.dueDate = `${formatDate(dueDate)}, ${formatTime(dueDate)}`; - } - if (record.proxy) { - record.proxy.name = getFullNameForCsvRecords(record.proxy); - } - if (record.deliveryAddress) { - const { addressLine1, city, region, postalCode, countryId } = record.deliveryAddress; - record.deliveryAddress = `${addressLine1 || ''} ${city || ''} ${region || ''} ${countryId || ''} ${postalCode || ''}`; - } - record.instance.contributorNames = contributorNamesMap.join('; '); - if (record.tags) record.tags.tagList = tagListMap.join('; '); - }); - - return result; - } - - // idType can be 'id', 'barcode', etc. - findResource(resource, value, idType = 'id') { - const query = urls[resource](value, idType); - - return fetch(`${this.okapiUrl}/${query}`, this.httpHeadersOptions).then(response => response.json()); - } - - toggleModal() { - this.setState({ errorMessage: '' }); - } - - // Called as a map function - addRequestFields(request) { - const { - requesterId, - instanceId, - itemId, - } = request; - const { titleLevelRequestsFeatureEnabled } = this.state; - - return Promise.all( - [ - this.findResource('user', requesterId), - this.findResource('requestsForInstance', instanceId), - ...(itemId - ? [ - this.findResource('requestsForItem', itemId), - ] - : []), - ], - ).then(([users, titleRequests, itemRequests]) => { - // Each element of the promises array returns an array of results, but in - // this case, there should only ever be one result for each. - const requester = get(users, 'users[0]', null); - const titleRequestCount = titleRequests?.requests.filter(r => r.requestLevel === REQUEST_LEVEL_TYPES.TITLE).length || 0; - const dynamicProperties = {}; - const requestsForFilter = titleLevelRequestsFeatureEnabled ? titleRequests.requests : itemRequests.requests; - - dynamicProperties.numberOfReorderableRequests = requestsForFilter.filter(currentRequest => isReorderableRequest(currentRequest)).length; - - if (itemId) { - dynamicProperties.itemRequestCount = get(itemRequests, 'totalRecords', 0); - } - - return { - ...request, - requester, - titleRequestCount, - ...dynamicProperties, - }; - }); - } - - getRowURL(id) { - const { - match: { path }, - location: { search }, - } = this.props; - - return `${path}/view/${id}${search}`; - } - - setURL = (id) => { - this.setState({ - selectedId: id, - }); - } - - resultIsSelected = ({ item }) => item.id === this.state.selectedId; - - viewRecordOnCollapse = () => { - this.setState({ - selectedId: null, - }); - } - - getHelperResourcePath = (helper, id) => `circulation/requests/${id}`; - - massageNewRecord = (requestData) => { - const { intl: { timeZone } } = this.props; - const isoDate = moment.tz(timeZone).toISOString(); - Object.assign(requestData, { requestDate: isoDate }); - }; - - renderPaneSub() { - const selectedRowsCount = size(this.state.selectedRows); - - return selectedRowsCount - ? ( - - ) - : null; - } - - onChangePatron = (patron) => { - this.props.mutator.activeRecord.update({ patronId: patron.id }); - }; - - create = (data) => { - const query = new URLSearchParams(this.props.location.search); - const mode = query.get('mode'); - - return this.props.mutator.records.POST(data) - .then(() => { - this.closeLayer(); - - this.context.sendCallout({ - message: isDuplicateMode(mode) - ? ( - - ) - : ( - - ), - }); - }) - .catch(resp => { - this.context.sendCallout({ - message: isDuplicateMode(mode) - ? - : , - type: 'error', - }); - - return this.processError(resp); - }); - }; - - processError(resp) { - const contentType = resp.headers.get('Content-Type') || ''; - if (contentType.startsWith('application/json')) { - return resp.json().then(error => this.handleJsonError(error)); - } else { - return resp.text().then(error => this.handleTextError(error)); - } - } - - handleTextError(error) { - const item = { barcode: error }; - return { item }; - } - - handleJsonError({ errors }) { - const { - intl, - } = this.props; - const errorMessages = []; - - errors.forEach((error) => ( - errorMessages.push(getRequestErrorMessage(error, intl)) - )); - - this.setState({ errorMessage: errorMessages.join(';') }); - } - - handleCloseNewRecord = (e) => { - if (e) { - e.preventDefault(); - } - - this.closeLayer(); - }; - - closeLayer() { - const url = buildUrl(this.props.location, { - layer: null, - itemBarcode: null, - userBarcode: null, - itemId: null, - instanceId: null, - userId: null, - query: null, - }); - - this.props.history.push(url); - } - - onDuplicate = (request) => { - const dupRequest = duplicateRequest(request); - - const newRequestData = { - layer: 'create', - instanceId: request.instanceId, - userBarcode: request.requester.barcode, - mode: createModes.DUPLICATE, - }; - - if (request.requestLevel === REQUEST_LEVEL_TYPES.ITEM) { - newRequestData.itemBarcode = request.item.barcode; - newRequestData.itemId = request.itemId; - } - if (request.requestLevel === REQUEST_LEVEL_TYPES.TITLE) { - dupRequest.createTitleLevelRequest = true; - } - - this.setState({ dupRequest }); - this.props.mutator.query.update(newRequestData); - }; - - buildRecordsForHoldsShelfReport = async () => { - const { - mutator: { - expiredHolds: { - reset, - GET, - }, - }, - } = this.props; - - this.setState({ - holdsShelfReportPending: true, - }); - - reset(); - - const { id } = this.getCurrentServicePointInfo(); - - this.setState({ - servicePointId: id, - }); - - if (id !== this.state.servicePointId) { - const path = `circulation/requests-reports/hold-shelf-clearance/${id}`; - const { requests } = await GET({ path }); - - this.setState({ - requests, - }); - } - - this.setState({ - holdsShelfReportPending: false, - }); - } - - exportExpiredHoldsToCSV = async () => { - const { - servicePointId, - requests, - } = this.state; - - if (!servicePointId) { - this.setState( - { - errorModalData: { - errorMessage: 'ui-requests.noServicePoint.errorMessage', - label: 'ui-requests.noServicePoint.label', - }, - } - ); - - return; - } - - const recordsToCSV = buildHoldRecords(requests); - exportCsv(recordsToCSV, { - onlyFields: this.expiredHoldsReportColumnHeaders, - excludeFields: ['id'], - }); - }; - - errorModalClose = () => { - this.setState({ errorModalData: {} }); - }; - - getPrintTemplate(slipType) { - const staffSlips = get(this.props.resources, 'staffSlips.records', []); - const slipTypeInLowerCase = slipType.toLowerCase(); - const slipTemplate = staffSlips.find(slip => slip.name.toLowerCase() === slipTypeInLowerCase); - - return DOMPurify.sanitize(get(slipTemplate, 'template', ''), { ADD_TAGS: ['Barcode'] }); - } - - handleFilterChange = ({ name, values }) => { - const { mutator } = this.props; - const newFilters = { - ...this.getActiveFilters(), - [name]: values, - }; - - const filters = Object.keys(newFilters) - .map((filterName) => { - return newFilters[filterName] - .map((filterValue) => `${filterName}.${filterValue}`) - .join(','); - }) - .filter(filter => filter) - .join(','); - - mutator.query.update({ filters }); - }; - - getActiveFilters = () => { - const { query } = this.props.resources; - - if (!query || !query.filters) return {}; - - return query.filters - .split(',') - .reduce((filterMap, currentFilter) => { - const [name, value] = currentFilter.split('.'); - - if (!Array.isArray(filterMap[name])) { - filterMap[name] = []; - } - - filterMap[name].push(value); - - return filterMap; - }, {}); - } - - renderFilters = (onChange) => { - const { resources } = this.props; - const { titleLevelRequestsFeatureEnabled, isViewPrintDetailsEnabled } = this.state; - - return ( - onChange({ name, values: [] })} - titleLevelRequestsFeatureEnabled={titleLevelRequestsFeatureEnabled} - isViewPrintDetailsEnabled={isViewPrintDetailsEnabled} - /> - ); - }; - - savePrintEventDetails = async (requestIds) => { - const currDateTime = new Date(); - const printTimeStamp = currDateTime.toISOString(); - const { id: loggedInUserId, username: loggedInUsername } = this.props.stripes.user.user; - - try { - await this.props.mutator.savePrintDetails.POST({ - 'requestIds': requestIds, - 'requesterName': loggedInUsername, - 'requesterId': loggedInUserId, - 'printEventDate': printTimeStamp - }); - } catch (error) { - // eslint-disable-next-line no-console - console.error('Failed to save print event details:', error); - } - }; - - onBeforeGetContentForPrintButton = (onToggle) => ( - new Promise(resolve => { - this.context.sendCallout({ message: }); - onToggle(); - // without the timeout the printing process starts right away - // and the callout and onToggle above are blocked - setTimeout(() => resolve(), 1000); - }) - ); - - onBeforeGetContentForSinglePrintButton = () => ( - new Promise(resolve => { - this.context.sendCallout({ message: }); - setTimeout(() => resolve(), 1000); - }) - ); - - onAfterPrintForPrintButton = () => { - if (this.state.isViewPrintDetailsEnabled) { - this.props.mutator.resultOffset.replace(0); - } - } - - printContentRefs = {}; - - getPrintContentRef = (rqId) => { - if (!this.printContentRefs[rqId]) { - this.printContentRefs[rqId] = React.createRef(); - } - - return this.printContentRefs[rqId]; - }; - - toggleRowSelection = row => { - this.setState(({ selectedRows }) => ({ selectedRows: getNextSelectedRowsState(selectedRows, row) })); - }; - - getPageTitle = () => { - const { - location, - intl: { - formatMessage, - }, - } = this.props; - const query = parse(location.search)?.query; - - if (query) { - return formatMessage({ id: 'ui-requests.documentTitle.search' }, { query }); - } - - return formatMessage({ id: 'ui-requests.meta.title' }); - } - - render() { - const { - resources, - mutator, - stripes, - history, - location, - intl, - stripes: { - timezone, - locale, - user: { user } - }, - } = this.props; - - const { - csvReportPending, - dupRequest, - errorMessage, - errorModalData, - requests, - servicePointId, - selectedRows, - holdsShelfReportPending, - createTitleLevelRequestsByDefault, - isViewPrintDetailsEnabled, - } = this.state; - const isPrintHoldRequestsEnabled = getPrintHoldRequestsEnabled(resources.printHoldRequests); - const { name: servicePointName } = this.getCurrentServicePointInfo(); - const pickSlips = get(resources, 'pickSlips.records', []); - const searchSlips = get(resources, 'searchSlips.records', []); - const patronGroups = get(resources, 'patronGroups.records', []); - const addressTypes = get(resources, 'addressTypes.records', []); - const servicePoints = get(resources, 'servicePoints.records', []); - const cancellationReasons = get(resources, 'cancellationReasons.records', []); - const requestCount = get(resources, 'records.other.totalRecords', 0); - const initialValues = dupRequest || - { - requestType: DEFAULT_REQUEST_TYPE_VALUE, - fulfillmentPreference: fulfillmentTypeMap.HOLD_SHELF, - createTitleLevelRequest: createTitleLevelRequestsByDefault, - }; - - const columnLabels = { - select: } - onChange={this.toggleAllRows} - />, - requestDate: , - title: , - year: , - itemBarcode: , - callNumber: , - type: , - requestStatus: , - position: , - servicePoint: , - requester: , - requesterBarcode: , - singlePrint: , - proxy: , - ...(isViewPrintDetailsEnabled && { - copies: , - printed: , - }), - }; - - const isPickSlipsArePending = resources?.pickSlips?.isPending; - const isSearchSlipsArePending = resources?.searchSlips?.isPending; - const requestsEmpty = isEmpty(requests); - const isPickSlipsEmpty = isEmpty(pickSlips); - const isSearchSlipsEmpty = isEmpty(searchSlips); - const pickSlipsPrintTemplate = this.getPrintTemplate(SLIPS_TYPE.PICK_SLIP); - const searchSlipsPrintTemplate = this.getPrintTemplate(SLIPS_TYPE.SEARCH_SLIP_HOLD_REQUESTS); - const pickSlipsData = convertToSlipData(pickSlips, intl, timezone, locale, SLIPS_TYPE.PICK_SLIP, user); - const searchSlipsData = convertToSlipData(searchSlips, intl, timezone, locale, SLIPS_TYPE.SEARCH_SLIP_HOLD_REQUESTS); - let multiSelectPickSlipData = getSelectedSlipDataMulti(pickSlipsData, selectedRows); - - const resultsFormatter = getListFormatter( - { - getRowURL: this.getRowURL, - setURL: this.setURL, - }, - { - intl, - selectedRows, - pickSlipsToCheck: pickSlips, - pickSlipsData, - isViewPrintDetailsEnabled, - getPrintContentRef: this.getPrintContentRef, - pickSlipsPrintTemplate, - toggleRowSelection: this.toggleRowSelection, - onBeforeGetContentForSinglePrintButton: this.onBeforeGetContentForSinglePrintButton, - onBeforePrintForSinglePrintButton: this.savePrintEventDetails, - onAfterPrintForSinglePrintButton: this.onAfterPrintForPrintButton, - } - ); - - const isPrintingDisabled = isPickSlipsEmpty || selectedRowsNonPrintable(pickSlipsData, selectedRows); - - const actionMenu = ({ onToggle, renderColumnsMenu }) => ( - <> - - - - - {csvReportPending ? - - - : - - } - { - isPickSlipsArePending ? - - - : - <> - - this.onBeforeGetContentForPrintButton(onToggle)} - onBeforePrint={async () => { - if (isViewPrintDetailsEnabled) { - const requestIds = extractPickSlipRequestIds(pickSlipsData); - await this.savePrintEventDetails(requestIds); - } - }} - onAfterPrint={this.onAfterPrintForPrintButton} - > - - - new Promise(resolve => { - this.context.sendCallout({ message: }); - onToggle(); - // without the timeout the printing process starts right away - // and the callout and onToggle above are blocked - setTimeout(() => resolve(), 1000); - multiSelectPickSlipData = getSelectedSlipDataMulti(pickSlipsData, selectedRows); - }) - } - onBeforePrint={ - async () => { - if (isViewPrintDetailsEnabled) { - const selectedPickSlips = getSelectedSlipDataMulti(pickSlipsData, selectedRows); - const selectedRequestIds = extractPickSlipRequestIds(selectedPickSlips); - await this.savePrintEventDetails(selectedRequestIds); - } - } - } - onAfterPrint={this.onAfterPrintForPrintButton} - > - - - - } - { - isPrintHoldRequestsEnabled && - <> - { - isSearchSlipsArePending ? - - - : - this.onBeforeGetContentForPrintButton(onToggle)} - > - - - } - - } - - {renderColumnsMenu} - - ); - - const columnManagerProps = { - excludeKeys: ['title', 'select'], - visibleColumns: [ - 'select', - 'title', - 'requestDate', - 'year', - 'itemBarcode', - 'type', - 'requestStatus', - 'position', - 'requester', - 'requesterBarcode', - 'proxy', - ], - }; - const pageTitle = this.getPageTitle(); - - return ( - - <> - { - isEmpty(errorModalData) || - - } - -
- -
- - - { - isPrintHoldRequestsEnabled && - - } - -
- ); - } -} - -export default stripesConnect(injectIntl(RequestsRoute)); diff --git a/src/deprecated/routes/RequestsRoute/RequestsRoute.test.js b/src/deprecated/routes/RequestsRoute/RequestsRoute.test.js deleted file mode 100644 index 76fb1d64..00000000 --- a/src/deprecated/routes/RequestsRoute/RequestsRoute.test.js +++ /dev/null @@ -1,1739 +0,0 @@ -import React from 'react'; -import { - stringify, -} from 'query-string'; -import { createIntl, createIntlCache } from 'react-intl'; - -import { - render, - screen, - waitFor, - cleanup, - fireEvent, -} from '@folio/jest-config-stripes/testing-library/react'; -import userEvent from '@folio/jest-config-stripes/testing-library/user-event'; - -import { - SearchAndSort, -} from '@folio/stripes/smart-components'; -import { - CalloutContext, - AppIcon, - TitleManager, -} from '@folio/stripes/core'; -import { - TextLink, - Checkbox, -} from '@folio/stripes/components'; -import { - exportCsv, - effectiveCallNumber, -} from '@folio/stripes/util'; - -import RequestsRoute, { - buildHoldRecords, - getListFormatter, - getPrintHoldRequestsEnabled, - getLastPrintedDetails, - getFilteredColumnHeadersMap, - urls, - DEFAULT_FORMATTER_VALUE, -} from './RequestsRoute'; -import CheckboxColumn from '../../../components/CheckboxColumn'; -import { - duplicateRequest, - getFullName, - getInstanceQueryString, - getNextSelectedRowsState, -} from '../../../utils'; -import { getTlrSettings } from '../../utils'; -import { - getFormattedYears, - getStatusQuery, -} from '../../../routes/utils'; -import { - createModes, - REQUEST_LEVEL_TYPES, - DEFAULT_REQUEST_TYPE_VALUE, - requestStatusesTranslations, - requestStatuses, - requestTypesTranslations, - requestTypesMap, - DEFAULT_DISPLAYED_YEARS_AMOUNT, - OPEN_REQUESTS_STATUSES, - MAX_RECORDS, - REQUEST_OPERATIONS, - INPUT_REQUEST_SEARCH_SELECTOR, - PRINT_DETAILS_COLUMNS, -} from '../../../constants'; -import { historyData } from '../../../../test/jest/fixtures/historyData'; - -const createRefMock = { - current: { - focus: jest.fn(), - }, -}; -const createDocumentRefMock = { - focus: jest.fn(), -}; -const testIds = { - searchAndSort: 'searchAndSort', - pickSlipsPrintTemplate: 'pickSlipsPrintTemplate', - searchSlipsPrintTemplate: 'searchSlipsPrintTemplate', - singlePrintButton: 'singlePrintButton', - rowCheckbox: 'rowCheckbox', - selectRequestCheckbox: 'selectRequestCheckbox', -}; - -const intlCache = createIntlCache(); -const intl = createIntl( - { - locale: 'en-US', - messages: {}, - }, - intlCache -); - -jest.spyOn(React, 'createRef').mockReturnValue(createRefMock); -jest.spyOn(document, 'getElementById').mockReturnValue(createDocumentRefMock); - -jest.mock('query-string', () => ({ - ...jest.requireActual('query-string'), - stringify: jest.fn(), -})); -jest.mock('../../../utils', () => ({ - ...jest.requireActual('../../../utils'), - duplicateRequest: jest.fn((request) => request), - getFullName: jest.fn(), - getFormattedYears: jest.fn(), - getInstanceQueryString: jest.fn(), - getNextSelectedRowsState: jest.fn(), - extractPickSlipRequestIds: jest.fn(), -})); -jest.mock('../../utils', () => ({ - getTlrSettings: jest.fn(() => ({})), -})); -jest.mock('../../../routes/utils', () => ({ - ...jest.requireActual('../../../routes/utils'), - getFormattedYears: jest.fn(), - getStatusQuery: jest.fn(), -})); -jest.mock('../../../components', () => ({ - ErrorModal: jest.fn(({ onClose }) => ( -
- ErrorModal - -
- )), - LoadingButton: jest.fn(() => null), - PrintButton: jest.fn(({ - onBeforeGetContent, - onBeforePrint, - onAfterPrint, - children, - }) => { - const handleClick = () => { - Promise.resolve(onBeforeGetContent()); - Promise.resolve(onBeforePrint()); - Promise.resolve(onAfterPrint()); - }; - return ( -
- - {children} -
- ); - }), - PrintContent: jest.fn(({ printContentTestId }) =>
PrintContent
) -})); -jest.mock('../../../components/RequestsFilters/RequestsFilters', () => ({ onClear }) => { - return ( -
- RequestsFilter - -
- ); -}); -jest.mock('../../components/ViewRequest/ViewRequest', () => jest.fn()); -jest.mock('../../components/RequestForm/RequestForm', () => jest.fn()); -jest.mock('../../components/RequestFormContainer/RequestFormContainer', () => jest.fn()); -jest.mock('../../../components/SinglePrintButtonForPickSlip', () => jest.fn(({ - onBeforeGetContentForSinglePrintButton, - onBeforePrintForSinglePrintButton, - onAfterPrintForSinglePrintButton, -}) => { - const handleClick = () => { - onBeforeGetContentForSinglePrintButton(); - onBeforePrintForSinglePrintButton(['reqId']); - onAfterPrintForSinglePrintButton(); - }; - return ( - - ); -})); -jest.mock('../../../components/CheckboxColumn', () => jest.fn(({ - toggleRowSelection, -}) => ( - -))); - -global.fetch = jest.fn(() => Promise.resolve({ - json: () => Promise.resolve({ - requests: [ - { - requestLevel: REQUEST_LEVEL_TYPES.TITLE, - } - ], - }), -})); - -const RequestFilterData = { - onChange: jest.fn(), -}; -const request = { - requesterId: 'requestId', - instanceId: 'instanceId', - itemId: 'itemId', -}; -const labelIds = { - closedCancelledRequest: requestStatusesTranslations[requestStatuses.CANCELLED], - requestType: requestTypesTranslations[requestTypesMap.RECALL], - printPickSlips: 'ui-requests.printPickSlips', - printSearchSlips: 'ui-requests.printSearchSlips', - titleWithSearch: 'ui-requests.documentTitle.search', - defaultTitle: 'ui-requests.meta.title', - recordsSelected: 'ui-requests.rows.recordsSelected', -}; -const mockedRequest = { - requestLevel: REQUEST_LEVEL_TYPES.ITEM, - itemId: 'itemId', - instanceId: 'instanceId', - item: { - barcode: 'itemBarcode', - }, - requester: { - barcode: 'requesterBarcode', - }, - id: 'requestId', -}; -const printDetailsMockData = { - printCount: 11, - printEventDate: '2024-08-03T13:33:31.868Z', - lastPrintRequester: { firstName: 'firstName', middleName: 'middleName', lastName: 'lastName' }, -}; - -SearchAndSort.mockImplementation(jest.fn(({ - paneTitleRef, - actionMenu, - detailProps: { - onDuplicate, - buildRecordsForHoldsShelfReport, - onChangePatron, - joinRequest, - }, - getHelperResourcePath, - massageNewRecord, - onCloseNewRecord, - onFilterChange, - parentResources, - renderFilters, - resultIsSelected, - viewRecordOnCollapse, - customPaneSub, - columnMapping, - resultsFormatter, -}) => { - const onClickActions = () => { - onDuplicate(parentResources.records.records[0]); - buildRecordsForHoldsShelfReport(); - massageNewRecord({}); - resultIsSelected({ - item: { - id: 'id', - }, - }); - viewRecordOnCollapse(); - }; - return ( - // eslint-disable-next-line jsx-a11y/click-events-have-key-events, jsx-a11y/no-static-element-interactions -
- {customPaneSub} -
-