From 89f6fc966fe98b787841c2c9c5f366b634ef3da1 Mon Sep 17 00:00:00 2001 From: Oleksandr Hladchenko1 Date: Fri, 10 May 2024 15:33:07 +0300 Subject: [PATCH] UIIN-2793: Populate Acquisitions accordion on instance when central ordering is active --- package.json | 4 +- .../InstanceAcquisition.js | 102 +++++++++++------- .../InstanceAcquisition.test.js | 86 ++++++++++++++- .../InstanceAcquisition/TenantAcquisition.css | 3 + .../InstanceAcquisition/TenantAcquisition.js | 58 ++++++++++ .../TenantAcquisition.test.js | 30 ++++++ .../InstanceAcquisition/fixtures.js | 1 + .../useInstanceAcquisition.js | 6 +- 8 files changed, 239 insertions(+), 51 deletions(-) create mode 100644 src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.css create mode 100644 src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.js create mode 100644 src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.test.js diff --git a/package.json b/package.json index 7cfe8c5db..bbdf02d70 100644 --- a/package.json +++ b/package.json @@ -889,7 +889,7 @@ "@babel/preset-react": "^7.9.0", "@folio/eslint-config-stripes": "^7.0.0", "@folio/jest-config-stripes": "^2.0.0", - "@folio/stripes": "^9.1.2", + "@folio/stripes": "^9.1.0", "@folio/stripes-cli": "^3.1.0", "@folio/stripes-components": "^12.1.2", "@folio/stripes-connect": "^9.1.0", @@ -936,7 +936,7 @@ "use-session-storage-state": "^18.2.0" }, "peerDependencies": { - "@folio/stripes": "^9.1.2", + "@folio/stripes": "^9.1.0", "@folio/stripes-marc-components": "^1.0.1", "react": "^18.2.0", "react-intl": "^6.4.4", diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.js b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.js index ab9da0d99..2da41c1b7 100644 --- a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.js +++ b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.js @@ -1,66 +1,86 @@ import React from 'react'; import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; -import { Link } from 'react-router-dom'; import { useStripes } from '@folio/stripes/core'; -import { - Accordion, - MultiColumnList, - NoValue, -} from '@folio/stripes/components'; +import { Accordion } from '@folio/stripes/components'; -import { getDateWithTime } from '../../../utils'; import { useControlledAccordion } from '../../../common'; +import { isUserInConsortiumMode } from '../../../utils'; import useInstanceAcquisition from './useInstanceAcquisition'; -const visibleColumns = ['poLineNumber', 'orderStatus', 'polReceiptStatus', 'dateOrdered', 'acqUnit', 'orderType']; -const columnMapping = { - poLineNumber: , - orderStatus: , - polReceiptStatus: , - dateOrdered: , - acqUnit: , - orderType: , -}; -const formatter = { - poLineNumber: i => {i.poLineNumber}, - orderStatus: i => ( - <> - {i.order?.workflowStatus ? : } - {i.order?.orderCloseReason?.reason && ` - ${i.order.orderCloseReason.reason}`} - - ), - polReceiptStatus: i => , - dateOrdered: i => getDateWithTime(i.order?.dateOrdered), - acqUnit: i => i.order?.acqUnits?.map(u => u.name)?.join(', ') || , - orderType: i => (i.order?.orderType ? : ), -}; +import TenantAcquisition from './TenantAcquisition'; + +import css from './TenantAcquisition.css'; const InstanceAcquisition = ({ accordionId, instanceId }) => { const stripes = useStripes(); - const { isLoading, instanceAcquisition } = useInstanceAcquisition(instanceId); - const controlledAccorion = useControlledAccordion(Boolean(instanceAcquisition?.length)); + const activeTenant = stripes.okapi.tenant; + const centralTenant = stripes.user.user?.consortium?.centralTenantId; + + const { + isLoading: isLoadingActiveTenantAcquisition, + instanceAcquisition: activeTenantAcquisition, + } = useInstanceAcquisition(instanceId, activeTenant); + const { + isLoading: isLoadingCentralTenantAcquisition, + instanceAcquisition: centralTenantAcquisition, + } = useInstanceAcquisition(instanceId, centralTenant); + + const controlledAcqAccorion = useControlledAccordion(Boolean(activeTenantAcquisition?.length || centralTenantAcquisition?.length)); + const controlledActiveTenantAcqAccorion = useControlledAccordion(Boolean(activeTenantAcquisition?.length)); + const controlledCetralTenantAcqAccorion = useControlledAccordion(Boolean(centralTenantAcquisition?.length)); if (!(stripes.hasInterface('order-lines') && stripes.hasInterface('orders') && stripes.hasInterface('acquisitions-units'))) return null; + const getTenantAccordionLabel = (tenants, id) => tenants?.find(tenant => tenant.id === id).name; + return ( } - {...controlledAccorion} + {...controlledAcqAccorion} > - + {isUserInConsortiumMode(stripes) ? ( + <> + + + + + {activeTenant !== centralTenant && ( + + + + )} + + ) : ( + + ) + } ); }; diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.test.js b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.test.js index dd0d570a9..b6b8aa05d 100644 --- a/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.test.js +++ b/src/Instance/InstanceDetails/InstanceAcquisition/InstanceAcquisition.test.js @@ -1,17 +1,30 @@ import React from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; +import { useStripes } from '@folio/stripes/core'; import { screen } from '@folio/jest-config-stripes/testing-library/react'; import '../../../../test/jest/__mock__'; -import renderWithIntl from '../../../../test/jest/helpers/renderWithIntl'; +import { + renderWithIntl, + translationsProperties, +} from '../../../../test/jest/helpers'; import { resultData, line } from './fixtures'; import InstanceAcquisition from './InstanceAcquisition'; import useInstanceAcquisition from './useInstanceAcquisition'; +import * as utils from '../../../utils'; + + +jest.mock('@folio/stripes/core', () => ({ + ...jest.requireActual('@folio/stripes/core'), + useStripes: jest.fn(), +})); jest.mock('./useInstanceAcquisition', () => jest.fn()); +const spyOnIsUserInConsortiumMode = jest.spyOn(utils, 'isUserInConsortiumMode'); + const renderInstanceAcquisition = (props = {}) => ( renderWithIntl( @@ -20,7 +33,8 @@ const renderInstanceAcquisition = (props = {}) => ( accordionId="accordionId" {...props} /> - + , + translationsProperties ) ); @@ -29,9 +43,71 @@ describe('InstanceAcquisition', () => { useInstanceAcquisition.mockClear().mockReturnValue({ instanceAcquisition: resultData }); }); - it('should display fetched instance acquisition data', () => { - renderInstanceAcquisition({ instanceId: 'instanceId' }); + describe('when user is non-consortial tenant', () => { + it('should display acquisition accordion and fetched instance acquisition data', () => { + spyOnIsUserInConsortiumMode.mockReturnValue(false); + useStripes.mockClear().mockReturnValue({ + hasInterface: () => true, + okapi: { tenant: 'diku' }, + user: { user: {} }, + }); + + const { container } = renderInstanceAcquisition({ instanceId: 'instanceId' }); + + expect(container.querySelector('#accordionId')).toBeInTheDocument(); + expect(screen.getByText(line.poLineNumber)).toBeInTheDocument(); + }); + }); + + describe('when user is in central tenant', () => { + it('should render central tenant subaccordion with fetched instance acquisition data', () => { + spyOnIsUserInConsortiumMode.mockReturnValue(true); + useStripes.mockClear().mockReturnValue({ + hasInterface: () => true, + okapi: { tenant: 'consortium' }, + user: { user: { + consortium: { centralTenantId: 'consortium' }, + tenants: [{ + id: 'consortium', + name: 'Consortium', + }], + } }, + }); + + const { container } = renderInstanceAcquisition({ instanceId: 'instanceId' }); + + expect(container.querySelector('#accordionId')).toBeInTheDocument(); + expect(container.querySelector('#active-tenant-acquisition-accordion')).toBeInTheDocument(); + expect(screen.getByText(line.poLineNumber)).toBeInTheDocument(); + }); + }); + + describe('when user is in member tenant', () => { + it('should render central and member tenant subaccordion with fetched instance acquisition data', () => { + spyOnIsUserInConsortiumMode.mockReturnValue(true); + useStripes.mockClear().mockReturnValue({ + hasInterface: () => true, + okapi: { tenant: 'college' }, + user: { user: { + consortium: { centralTenantId: 'consortium' }, + tenants: [{ + id: 'consortium', + name: 'Consortium', + }, { + id: 'college', + name: 'College', + }], + } }, + }); + + const { container } = renderInstanceAcquisition({ instanceId: 'instanceId' }); + + expect(container.querySelector('#accordionId')).toBeInTheDocument(); + expect(container.querySelector('#active-tenant-acquisition-accordion')).toBeInTheDocument(); + expect(screen.getAllByText(line.poLineNumber)[0]).toBeInTheDocument(); - expect(screen.getByText(line.poLineNumber)).toBeInTheDocument(); + expect(container.querySelector('#central-tenant-acquisition-accordion')).toBeInTheDocument(); + expect(screen.getAllByText(line.poLineNumber)[1]).toBeInTheDocument(); + }); }); }); diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.css b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.css new file mode 100644 index 000000000..b450395ee --- /dev/null +++ b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.css @@ -0,0 +1,3 @@ +.tenantAcquisitionAccordion { + padding-left: 15px; +} diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.js b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.js new file mode 100644 index 000000000..c34595b98 --- /dev/null +++ b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.js @@ -0,0 +1,58 @@ +import React from 'react'; +import PropTypes from 'prop-types'; +import { FormattedMessage } from 'react-intl'; +import { Link } from 'react-router-dom'; + +import { + MultiColumnList, + NoValue, +} from '@folio/stripes/components'; + +import { getDateWithTime } from '../../../utils'; + +const visibleColumns = ['poLineNumber', 'orderStatus', 'polReceiptStatus', 'dateOrdered', 'acqUnit', 'orderType']; +const columnMapping = { + poLineNumber: , + orderStatus: , + polReceiptStatus: , + dateOrdered: , + acqUnit: , + orderType: , +}; +const formatter = { + poLineNumber: i => {i.poLineNumber}, + orderStatus: i => ( + <> + {i.order?.workflowStatus ? : } + {i.order?.orderCloseReason?.reason && ` - ${i.order.orderCloseReason.reason}`} + + ), + polReceiptStatus: i => , + dateOrdered: i => getDateWithTime(i.order?.dateOrdered), + acqUnit: i => i.order?.acqUnits?.map(u => u.name)?.join(', ') || , + orderType: i => (i.order?.orderType ? : ), +}; + +const TenantAcquisition = ({ + acquisitions, + isLoading, + tenantId, +}) => ( + +); + +TenantAcquisition.propTypes = { + acquisitions: PropTypes.arrayOf(PropTypes.object).isRequired, + isLoading: PropTypes.bool.isRequired, + tenantId: PropTypes.string.isRequired, +}; + +export default TenantAcquisition; diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.test.js b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.test.js new file mode 100644 index 000000000..53005b5bd --- /dev/null +++ b/src/Instance/InstanceDetails/InstanceAcquisition/TenantAcquisition.test.js @@ -0,0 +1,30 @@ +import React from 'react'; +import { BrowserRouter as Router } from 'react-router-dom'; + +import { screen } from '@folio/jest-config-stripes/testing-library/react'; + +import '../../../../test/jest/__mock__'; +import { renderWithIntl } from '../../../../test/jest/helpers'; + +import { resultData, line } from './fixtures'; +import TenantAcquisition from './TenantAcquisition'; + +const renderTenantAcquisition = () => ( + renderWithIntl( + + + + ) +); + +describe('TenantAcquisition', () => { + it('should display instance acquisition data', () => { + renderTenantAcquisition(); + + expect(screen.getByText(line.poLineNumber)).toBeInTheDocument(); + }); +}); diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/fixtures.js b/src/Instance/InstanceDetails/InstanceAcquisition/fixtures.js index 626a29e23..88e69d1c0 100644 --- a/src/Instance/InstanceDetails/InstanceAcquisition/fixtures.js +++ b/src/Instance/InstanceDetails/InstanceAcquisition/fixtures.js @@ -2,6 +2,7 @@ export const line = { id: 'lineId', purchaseOrderId: 'orderId', poLineNumber: '1000', + receiptStatus: 'Ongoing', }; export const order = { id: line.purchaseOrderId, diff --git a/src/Instance/InstanceDetails/InstanceAcquisition/useInstanceAcquisition.js b/src/Instance/InstanceDetails/InstanceAcquisition/useInstanceAcquisition.js index 691bcecdb..b472ec587 100644 --- a/src/Instance/InstanceDetails/InstanceAcquisition/useInstanceAcquisition.js +++ b/src/Instance/InstanceDetails/InstanceAcquisition/useInstanceAcquisition.js @@ -6,13 +6,13 @@ import { useOkapiKy, useNamespace, useStripes } from '@folio/stripes/core'; import { LIMIT_MAX } from '../../../constants'; import { batchRequest } from '../../../utils'; -const useInstanceAcquisition = (id) => { - const ky = useOkapiKy(); +const useInstanceAcquisition = (id, tenant) => { + const ky = useOkapiKy({ tenant }); const [namespace] = useNamespace({ key: 'instance-acquisition' }); const stripes = useStripes(); const { data = [], isLoading } = useQuery( - [namespace, 'instance-acquisition', id], + [namespace, 'instance-acquisition', id, tenant], async () => { const { titles = [] } = await ky.get('orders/titles', { searchParams: {