diff --git a/CHANGELOG.md b/CHANGELOG.md index a8fc9be68..fdc138cf7 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,8 @@ # Change history for ui-inventory +## 10.0.1 IN PROGRESS +* Enable/disable consortial holdings/item actions based on User permissions. Refs UIIN-2452. + ## 10.0.0 IN PROGRESS * Added a new option in the "Actions" dropdown within the Inventory app search page for "+New MARC Bib Record". Refs UIIN-2356. * Change the url for the "New MARC Bib Record" page. Refs UIIN-2380. @@ -100,7 +103,11 @@ * Instance 3rd pane: Add consortial holdings/item accordion. Refs UIIN-2410. * Consortial Central Tenant: Handling Holdings and Item actions on the Instance detail view. Refs UIIN-2523. * Hide Held by facet in Inventory contributor and subject browse. Refs UIIN-2591. -* Enable/disable consortial holdings/item actions based on User permissions. Refs UIIN-2452. +* Use `==` for exact phrase search in Advanced Search for all full-text and term fields. Refs UIIN-2612. +* Provide an instance `tenantId` to the PO line form when creating an order from the instance. Refs UIIN-2614. +* Bump @folio/stripes-acq-components dependency version to 5.0.0. Refs UIIN-2620. +* ECS: Check when sharing instance with source=MARC is complete before re-fetching it. Refs UIIN-2605. +* Update all facets after changing a term or selecting a facet option. Refs UIIN-2610. ## [9.4.12](https://github.com/folio-org/ui-inventory/tree/v9.4.12) (2023-09-21) [Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.11...v9.4.12) diff --git a/package.json b/package.json index c005c64bc..285aa1008 100644 --- a/package.json +++ b/package.json @@ -871,7 +871,7 @@ "eslint-plugin-filenames": "^1.3.2", "eslint-plugin-import": "^2.22.1", "eslint-plugin-jest": "^24.0.0", - "inflected": "^2.0.4", + "inflected": "^2.1.0", "react": "^18.2.0", "react-dom": "^18.2.0", "react-intl": "^6.4.4", @@ -883,7 +883,7 @@ }, "dependencies": { "@folio/quick-marc": "^7.0.0", - "@folio/stripes-acq-components": "^4.0.0", + "@folio/stripes-acq-components": "^5.0.0", "classnames": "^2.3.2", "file-saver": "^2.0.0", "final-form": "^4.18.2", diff --git a/src/Instance/InstanceDetails/InstanceDetails.js b/src/Instance/InstanceDetails/InstanceDetails.js index 7c0d0c7de..f24701aca 100644 --- a/src/Instance/InstanceDetails/InstanceDetails.js +++ b/src/Instance/InstanceDetails/InstanceDetails.js @@ -19,6 +19,7 @@ import { PaneMenu, Row, MessageBanner, + Icon, } from '@folio/stripes/components'; import { InstanceTitle } from './InstanceTitle'; @@ -36,9 +37,17 @@ import { InstanceNewHolding } from './InstanceNewHolding'; import { InstanceAcquisition } from './InstanceAcquisition'; import HelperApp from '../../components/HelperApp'; -import { getAccordionState } from './utils'; import { DataContext } from '../../contexts'; import { ConsortialHoldings } from './ConsortialHoldings'; +import { + getAccordionState, + getPublishingInfo, +} from './utils'; +import { + getDate, + isInstanceShadowCopy, + isUserInConsortiumMode, +} from '../../utils'; const accordions = { administrative: 'acc01', @@ -57,23 +66,25 @@ const accordions = { const InstanceDetails = forwardRef(({ children, instance, - paneTitle, - paneSubtitle, onClose, actionMenu, tagsEnabled, userTenantPermissions, + isShared, + isLoading, ...rest }, ref) => { + const intl = useIntl(); const stripes = useStripes(); const { okapi: { tenant: tenantId } } = stripes; - const intl = useIntl(); + const location = useLocation(); const searchParams = new URLSearchParams(location.search); const referenceData = useContext(DataContext); const accordionState = useMemo(() => getAccordionState(instance, accordions), [instance]); const [helperApp, setHelperApp] = useState(); + const tags = instance?.tags?.tagList; const isUserInCentralTenant = checkIfUserInCentralTenant(stripes); @@ -97,14 +108,61 @@ const InstanceDetails = forwardRef(({ ); }, [tagsEnabled, tags, intl]); + if (isLoading) { + return ( + } + dismissible + onClose={onClose} + > +
+ +
+
+ ); + } + + const renderPaneTitle = () => { + const isInstanceShared = Boolean(isShared || isInstanceShadowCopy(instance?.source)); + + return ( + + ); + }; + + const renderPaneSubtitle = () => { + return ( + + ); + }; + return ( <> } - paneTitle={paneTitle} - paneSub={paneSubtitle} + paneTitle={renderPaneTitle()} + paneSub={renderPaneSubtitle()} dismissible onClose={onClose} actionMenu={actionMenu} @@ -243,16 +301,18 @@ InstanceDetails.propTypes = { actionMenu: PropTypes.func, onClose: PropTypes.func.isRequired, instance: PropTypes.object, - paneTitle: PropTypes.object, - paneSubtitle: PropTypes.oneOfType([PropTypes.string, PropTypes.element]), tagsToggle: PropTypes.func, tagsEnabled: PropTypes.bool, userTenantPermissions: PropTypes.arrayOf(PropTypes.object), + isLoading: PropTypes.bool, + isShared: PropTypes.bool, }; InstanceDetails.defaultProps = { instance: {}, tagsEnabled: false, + isLoading: false, + isShared: false, }; export default InstanceDetails; diff --git a/src/Instance/InstanceDetails/utils.js b/src/Instance/InstanceDetails/utils.js index 1d9b3bbbb..b97ad2555 100644 --- a/src/Instance/InstanceDetails/utils.js +++ b/src/Instance/InstanceDetails/utils.js @@ -20,7 +20,7 @@ export const getPublishingInfo = instance => { return undefined; }; -export const getAccordionState = (instance, accordions) => { +export const getAccordionState = (instance = {}, accordions = {}) => { const instanceData = pick( instance, ['hrid', 'source', 'catalogedDate', 'statusId', 'statusUpdatedDate', 'modeOfIssuanceId', 'statisticalCodeIds'], @@ -48,13 +48,13 @@ export const getAccordionState = (instance, accordions) => { return ({ [accordions.administrative]: !areAllFieldsEmpty(values(instanceData)), [accordions.title]: !areAllFieldsEmpty(values(titleData)), - [accordions.identifiers]: !areAllFieldsEmpty([instance.identifiers ?? []]), - [accordions.contributors]: !areAllFieldsEmpty([instance.contributors ?? []]), + [accordions.identifiers]: !areAllFieldsEmpty([instance?.identifiers ?? []]), + [accordions.contributors]: !areAllFieldsEmpty([instance?.contributors ?? []]), [accordions.descriptiveData]: !areAllFieldsEmpty(values(descriptiveData)), [accordions.notes]: !areAllFieldsEmpty([instance?.notes ?? []]), - [accordions.electronicAccess]: !areAllFieldsEmpty([instance.electronicAccess ?? []]), - [accordions.subjects]: !areAllFieldsEmpty([instance.subjects ?? []]), - [accordions.classifications]: !areAllFieldsEmpty([instance.classifications ?? []]), + [accordions.electronicAccess]: !areAllFieldsEmpty([instance?.electronicAccess ?? []]), + [accordions.subjects]: !areAllFieldsEmpty([instance?.subjects ?? []]), + [accordions.classifications]: !areAllFieldsEmpty([instance?.classifications ?? []]), [accordions.relationship]: !areAllFieldsEmpty(values(instanceRelationship)), }); }; diff --git a/src/ViewInstance.js b/src/ViewInstance.js index 6f1251da9..e736d5cf2 100644 --- a/src/ViewInstance.js +++ b/src/ViewInstance.js @@ -9,18 +9,16 @@ import { import { flowRight, isEmpty, + pick, } from 'lodash'; import { - AppIcon, Pluggable, stripesConnect, checkIfUserInMemberTenant, checkIfUserInCentralTenant, } from '@folio/stripes/core'; import { - Pane, - Icon, MenuSection, Callout, checkScope, @@ -34,18 +32,19 @@ import ViewHoldingsRecord from './ViewHoldingsRecord'; import makeConnectedInstance from './ConnectedInstance'; import withLocation from './withLocation'; import InstancePlugin from './components/InstancePlugin'; -import { getPublishingInfo } from './Instance/InstanceDetails/utils'; import { getDate, getUserTenantsPermissions, handleKeyCommand, isInstanceShadowCopy, isMARCSource, - isUserInConsortiumMode, } from './utils'; import { + AUTHORITY_LINKED_FIELDS, indentifierTypeNames, + INSTANCE_SHARING_STATUSES, layers, + OKAPI_TENANT_HEADER, REQUEST_OPEN_STATUSES, } from './constants'; import { DataContext } from './contexts'; @@ -177,18 +176,22 @@ class ViewInstance extends React.Component { this.log = logger.log.bind(logger); this.state = { + isLoading: false, marcRecord: null, findInstancePluginOpened: false, isItemsMovement: false, isImportRecordModalOpened: false, isCopyrightModalOpened: false, isShareLocalInstanceModalOpen: false, + isUnlinkAuthoritiesModalOpen: false, + linkedAuthoritiesLength: 0, isNewOrderModalOpen: false, afterCreate: false, instancesQuickExportInProgress: false, userTenantPermissions: [], }; this.instanceId = null; + this.intervalId = null; this.cViewHoldingsRecord = this.props.stripes.connect(ViewHoldingsRecord); this.calloutRef = createRef(); @@ -253,6 +256,7 @@ class ViewInstance extends React.Component { componentWillUnmount() { this.props.mutator.allInstanceItems.reset(); + clearInterval(this.intervalId); } getCurrentTenantPermissions = () => { @@ -432,6 +436,48 @@ class ViewInstance extends React.Component { this.setState({ isImportRecordModalOpened: false }); } + checkInstanceSharingProgress = ({ sourceTenantId, instanceIdentifier }) => { + return this.props.mutator.shareInstance.GET({ + params: { sourceTenantId, instanceIdentifier }, + headers: { + [OKAPI_TENANT_HEADER]: this.props.centralTenantId, + 'Content-Type': 'application/json', + ...(this.props.okapi.token && { 'X-Okapi-Token': this.props.okapi.token }), + }, + }); + } + + waitForInstanceSharingComplete = ({ sourceTenantId, instanceIdentifier, instanceTitle }) => { + return new Promise((resolve, reject) => { + this.intervalId = setInterval(() => { + const onError = error => { + this.calloutRef.current.sendCallout({ + type: 'error', + message: , + }); + clearInterval(this.intervalId); + reject(error); + }; + const onSuccess = response => { + const sharingStatus = response?.sharingInstances[0]?.status; + + if (sharingStatus === INSTANCE_SHARING_STATUSES.COMPLETE) { + clearInterval(this.intervalId); + resolve(response); + } + + if (sharingStatus === INSTANCE_SHARING_STATUSES.ERROR) { + onError(response); + } + }; + + this.checkInstanceSharingProgress({ sourceTenantId, instanceIdentifier }) + .then(onSuccess) + .catch(onError); + }, 2000); + }); + } + handleShareLocalInstance = (instance = {}) => { const centralTenantId = this.props.centralTenantId; const sourceTenantId = this.props.okapi.tenant; @@ -443,24 +489,66 @@ class ViewInstance extends React.Component { instanceIdentifier, targetTenantId: centralTenantId, }) + .then(async () => { + this.setState({ + isUnlinkAuthoritiesModalOpen: false, + isShareLocalInstanceModalOpen: false, + isLoading: true + }); + + await this.waitForInstanceSharingComplete({ sourceTenantId, instanceIdentifier, instanceTitle }); + }) .then(async () => { await this.props.refetchInstance(); + this.setState({ isLoading: false }); this.calloutRef.current.sendCallout({ type: 'success', message: , }); }) .catch(() => { - this.calloutRef.current.sendCallout({ + this.setState({ + isUnlinkAuthoritiesModalOpen: false, + isShareLocalInstanceModalOpen: false, + }); + this.calloutRef.current?.sendCallout({ type: 'error', message: , }); - }) - .finally(() => { - this.setState({ isShareLocalInstanceModalOpen: false }); }); } + checkIfHasLinkedAuthorities = (instance = {}) => { + const linkedAuthorities = pick(this.props.selectedInstance, AUTHORITY_LINKED_FIELDS); + + const findLinkedAuthorities = authorities => { + const linkedAuthoritiesLength = AUTHORITY_LINKED_FIELDS.reduce((total, field) => { + const authoritiesAmount = authorities[field].filter(item => item.authorityId).length; + return total + authoritiesAmount; + }, 0); + + return { + hasLinkedAuthorities: !!linkedAuthoritiesLength, + linkedAuthoritiesLength, + }; + }; + + const { + hasLinkedAuthorities, + linkedAuthoritiesLength, + } = findLinkedAuthorities(linkedAuthorities); + + if (hasLinkedAuthorities) { + this.setState({ + linkedAuthoritiesLength, + isShareLocalInstanceModalOpen: false, + isUnlinkAuthoritiesModalOpen: true, + }); + } else { + this.handleShareLocalInstance(instance); + } + } + toggleCopyrightModal = () => { this.setState(prevState => ({ isCopyrightModalOpened: !prevState.isCopyrightModalOpened })); }; @@ -783,26 +871,6 @@ class ViewInstance extends React.Component { ); }; - renderPaneTitle = (instance) => { - const { - stripes, - isShared, - } = this.props; - - const isInstanceShared = Boolean(isShared || isInstanceShadowCopy(instance?.source)); - - return ( - - ); - }; - render() { const { match: { params: { id, holdingsrecordid, itemid } }, @@ -810,13 +878,13 @@ class ViewInstance extends React.Component { okapi, onCopy, onClose, - paneWidth, tagsEnabled, updateLocation, canUseSingleRecordImport, - intl, isCentralTenantPermissionsLoading, + isShared, } = this.props; + const { linkedAuthoritiesLength } = this.state; const ci = makeConnectedInstance(this.props, stripes.logger); const instance = ci.instance(); @@ -850,26 +918,7 @@ class ViewInstance extends React.Component { handler: (e) => collapseAllSections(e, this.accordionStatusRef), }, ]; - - if (!instance || isCentralTenantPermissionsLoading) { - return ( - } - dismissible - onClose={onClose} - > -
- -
-
- ); - } + const isInstanceLoading = this.state.isLoading || !instance || isCentralTenantPermissionsLoading; return ( @@ -881,22 +930,14 @@ class ViewInstance extends React.Component { > - } onClose={onClose} actionMenu={this.createActionMenuGetter(instance, data)} instance={instance} tagsEnabled={tagsEnabled} ref={this.accordionStatusRef} userTenantPermissions={this.state.userTenantPermissions} + isLoading={isInstanceLoading} + isShared={isShared} > { (!holdingsrecordid && !itemid) ? @@ -919,7 +960,7 @@ class ViewInstance extends React.Component { {this.state.afterCreate && } + message={} /> } @@ -953,6 +994,15 @@ class ViewInstance extends React.Component { message={} confirmLabel={} onCancel={() => this.setState({ isShareLocalInstanceModalOpen: false })} + onConfirm={() => this.checkIfHasLinkedAuthorities(instance)} + /> + + } + message={} + confirmLabel={} + onCancel={() => this.setState({ isUnlinkAuthoritiesModalOpen: false })} onConfirm={() => this.handleShareLocalInstance(instance)} /> @@ -1005,11 +1055,13 @@ ViewInstance.propTypes = { GET: PropTypes.func.isRequired, reset: PropTypes.func.isRequired, }).isRequired, - shareInstance: PropTypes.shape({ POST: PropTypes.func.isRequired }).isRequired, + shareInstance: PropTypes.shape({ + POST: PropTypes.func.isRequired, + GET: PropTypes.func.isRequired, + }).isRequired, }), onClose: PropTypes.func, onCopy: PropTypes.func, - paneWidth: PropTypes.string.isRequired, resources: PropTypes.shape({ allInstanceItems: PropTypes.object.isRequired, allInstanceHoldings: PropTypes.object.isRequired, diff --git a/src/ViewInstance.test.js b/src/ViewInstance.test.js index 9d8610088..188eb8000 100644 --- a/src/ViewInstance.test.js +++ b/src/ViewInstance.test.js @@ -186,7 +186,10 @@ const defaultProp = { GET: jest.fn(() => Promise.resolve([])), reset: jest.fn() }, - shareInstance: { POST: jest.fn() }, + shareInstance: { + POST: jest.fn(), + GET: jest.fn(() => Promise.resolve({ sharingInstances: [{ status: 'COMPLETE' }] })), + }, }, onClose: mockonClose, onCopy: jest.fn(), @@ -281,6 +284,7 @@ describe('ViewInstance', () => { ok: true, json: async () => ({}), }); + useStripes().hasInterface.mockReturnValue(true); }); it('should display action menu items', () => { renderViewInstance(); @@ -295,7 +299,7 @@ describe('ViewInstance', () => { describe('instance header', () => { describe('for non-consortia users', () => { it('should render instance title, publisher, and publication date', () => { - defaultProp.stripes.hasInterface.mockReturnValue(false); + useStripes().hasInterface.mockReturnValue(false); const { getByText } = renderViewInstance(); const expectedTitle = 'Instance • #youthaction • Information Age Publishing, Inc. • 2015'; @@ -581,27 +585,65 @@ describe('ViewInstance', () => { }); describe('when clicking Share local instance', () => { - it('should show confirmation modal', () => { - checkIfUserInMemberTenant.mockClear().mockReturnValue(true); - - renderViewInstance(); - fireEvent.click(screen.getByRole('button', { name: 'Actions' })); - fireEvent.click(screen.getByRole('button', { name: 'Share local instance' })); - - expect(screen.getByText('Confirmation modal')).toBeInTheDocument(); - }); - - describe('when confirming', () => { - it('should make POST request to share local instance', () => { - defaultProp.mutator.shareInstance.POST.mockResolvedValue({}); + describe('and it has no linked MARC Authorities', () => { + it('should show confirmation modal', () => { checkIfUserInMemberTenant.mockClear().mockReturnValue(true); renderViewInstance(); fireEvent.click(screen.getByRole('button', { name: 'Actions' })); fireEvent.click(screen.getByRole('button', { name: 'Share local instance' })); - fireEvent.click(screen.getByRole('button', { name: 'Confirm' })); - expect(defaultProp.mutator.shareInstance.POST).toHaveBeenCalled(); + expect(screen.getByText('Confirmation modal')).toBeInTheDocument(); + }); + }); + + describe('when confirming', () => { + describe('and it has linked MARC Authorities', () => { + beforeEach(() => { + defaultProp.mutator.shareInstance.POST.mockResolvedValue({}); + checkIfUserInMemberTenant.mockClear().mockReturnValue(true); + + renderViewInstance({ + selectedInstance: { + ...instances[0], + alternativeTitles: [{ + alternativeTitleTypeId: 'fe19bae4-da28-472b-be90-d442e2428ead', + alternativeTitle: 'Hashtag youthaction', + authorityId: 'testAuthorityId' + }], + } + }); + }); + + it('should render unlink local MARC Authorities modal', () => { + fireEvent.click(screen.getByRole('button', { name: 'Actions' })); + fireEvent.click(screen.getByRole('button', { name: 'Share local instance' })); + fireEvent.click(screen.getByRole('button', { name: 'Confirm' })); + + expect(screen.getByText('Confirmation modal')).toBeInTheDocument(); + }); + + describe('when proceed sharing', () => { + it('should make POST request to share local instance', () => { + fireEvent.click(screen.getByRole('button', { name: 'Actions' })); + fireEvent.click(screen.getByRole('button', { name: 'Share local instance' })); + fireEvent.click(screen.getByRole('button', { name: 'Confirm' })); + fireEvent.click(screen.getByRole('button', { name: 'Confirm' })); + + expect(defaultProp.mutator.shareInstance.POST).toHaveBeenCalled(); + }); + }); + + describe('when cancel sharing', () => { + it('should hide confirmation modal', () => { + fireEvent.click(screen.getByRole('button', { name: 'Actions' })); + fireEvent.click(screen.getByRole('button', { name: 'Share local instance' })); + fireEvent.click(screen.getByRole('button', { name: 'Confirm' })); + fireEvent.click(screen.getByRole('button', { name: 'Cancel' })); + + expect(screen.queryByText('Confirmation modal')).not.toBeInTheDocument(); + }); + }); }); }); }); diff --git a/src/common/hooks/useFacets.js b/src/common/hooks/useFacets.js index 916259359..a8ef5e659 100644 --- a/src/common/hooks/useFacets.js +++ b/src/common/hooks/useFacets.js @@ -4,15 +4,27 @@ import _ from 'lodash'; import { useFacetSettings } from '../../stores/facetsStore'; +// Facets behavior (useFacets and withFacets): +// - when the user opens a facet, the first 6 options must be fetched for it; +// - when the user clicks the "+More" button under the options, all options for that facet must be fetched; +// - when the user places the cursor in a facet's input field, all options for it must be fetched; +// - when multiple facets are open and the user enters a value in the search box, options must be fetched for all open facets. +// - when multiple facets are open and the user selects an option of any facet, options must be fetched for all open facets. + const useFacets = ( segmentAccordions, segmentOptions, selectedFacetFilters, getNewRecords, data, + isFetchFacetsAfterReset = true, ) => { const { - query: { query, filters = '' }, + query: { + query, + qindex, + filters = '', + }, onFetchFacets, parentResources: { facets }, } = data; @@ -30,7 +42,9 @@ const useFacets = ( const prevAccordionsState = useRef(accordions); const prevFilters = useRef({}); const prevUrl = useRef({}); - const prevQuery = useRef(''); + const prevQindex = useRef(''); + const isSearchOptionChanged = useRef(false); + const isReset = useRef(false); const onToggleSection = useCallback(({ id }) => { setAccordions(curState => { @@ -40,6 +54,15 @@ const useFacets = ( }); }, []); + const onUnregisterAccordion = useCallback((id) => { + setAccordions(curState => { + const newState = _.cloneDeep(curState); + newState[id] = false; + return newState; + }); + delete prevUrl.current[id]; + }, []); + const handleFilterSearch = useCallback((filter) => { const { name, @@ -146,6 +169,12 @@ const useFacets = ( showLoadingForAllFacets ]); + useEffect(() => { + isSearchOptionChanged.current = !query && !filters && qindex && prevQindex.current && qindex !== prevQindex.current; + isReset.current = !query && !filters && !qindex; + prevQindex.current = qindex; + }, [qindex, filters, query]); + useEffect(() => { if (!_.isEmpty(records)) { const newRecords = getNewRecords(records); @@ -155,17 +184,12 @@ const useFacets = ( useEffect(() => { let facetToOpen = ''; - let facetToClose = ''; const isFacetOpened = _.some(prevAccordionsState.current, (prevFacetValue, facetName) => { const curFacetValue = accordions[facetName]; - if (curFacetValue !== prevFacetValue) { - if (curFacetValue) { - facetToOpen = facetName; - } else { - facetToClose = facetName; - } + if (curFacetValue !== prevFacetValue && curFacetValue) { + facetToOpen = facetName; return curFacetValue; } return false; @@ -180,8 +204,7 @@ const useFacets = ( !facetToOpen.match(/updatedDate/i) ) { handleFetchFacets({ facetToOpen }); - } else { - prevUrl.current[facetToClose] = location.search; + prevUrl.current[facetToOpen] = location.search; } prevAccordionsState.current = { ...accordions }; @@ -189,10 +212,16 @@ const useFacets = ( useEffect(() => { if (!_.isEmpty(accordionsData)) { - const isNoFilterSelected = _.every(accordionsData, value => !value?.isSelected); - if (!query && prevQuery.current && isNoFilterSelected) return; + // When there is a value in the search box and any facet option is selected and the user resets the search, + // and `isFetchFacetsAfterReset` is true, then two useEffects are called, one is tracking `query` and another is + // tracking `accordionsData`, hence 2 calls are fired, so let's check url to make only 1 call. + const areOpenFacetsAlreadyFetched = prevUrl.current.all === location.search; - handleFetchFacets({ focusedFacet: facetNameToOpen }); + if (isSearchOptionChanged.current || (!isFetchFacetsAfterReset && isReset.current) || areOpenFacetsAlreadyFetched) { + return; + } + handleFetchFacets(); + prevUrl.current.all = location.search; } }, [accordionsData]); @@ -204,15 +233,14 @@ const useFacets = ( useEffect(() => { const isSomeFacetOpened = _.some(accordions, isFacetOpened => isFacetOpened); - const isValidQuery = (query && query !== prevQuery.current) || (query !== undefined && prevQuery.current); + + if (isSearchOptionChanged.current || (!isFetchFacetsAfterReset && isReset.current)) { + return; + } if (isSomeFacetOpened) { - if (isValidQuery) { - prevQuery.current = query; - handleFetchFacets({ facetToOpen: facetNameToOpen }); - } - } else if (isValidQuery) { - prevQuery.current = query; + handleFetchFacets(); + prevUrl.current.all = location.search; } }, [query]); @@ -223,6 +251,7 @@ const useFacets = ( handleFilterSearch, facetsOptions, getIsPending, + onUnregisterAccordion, ]; }; diff --git a/src/common/hooks/useFacets.test.js b/src/common/hooks/useFacets.test.js index 66f8bb28d..17d01ba0b 100644 --- a/src/common/hooks/useFacets.test.js +++ b/src/common/hooks/useFacets.test.js @@ -43,6 +43,378 @@ describe('useFacets', () => { useFacetSettings.mockReturnValue([selectedFacetFilters, jest.fn()]); }); + describe('onFetchFacets', () => { + const _segmentAccordions = { + resource: false, + format: false, + }; + + const _segmentOptions = { + resourceTypeOptions: [], + instanceFormatOptions: [], + }; + + const _selectedFacetFilters = { + resource: undefined, + format: undefined, + }; + + const _getNewRecords = jest.fn(); + + const _data = { + instanceFormats: [ + { + 'id': '8d511d33-5e85-4c5d-9bce-6e3c9cd0c324', + 'name': 'unmediated -- volume', + 'code': 'nc', + 'source': 'rdacarrier', + }, + { + 'id': 'f5e8210f-7640-459b-a71f-552567f92369', + 'name': 'computer -- online resource', + 'code': 'cr', + 'source': 'rdacarrier', + }, { + 'id': '5cb91d15-96b1-4b8a-bf60-ec310538da66', + 'name': 'audio -- audio disc', + 'code': 'sd', + 'source': 'rdacarrier', + } + ], + resourceTypes: [ + { + 'id': '6312d172-f0cf-40f6-b27d-9fa8feaf332f', + 'name': 'text', + 'code': 'txt', + 'source': 'rdacontent', + }, + { + 'id': '30fffe0e-e985-4144-b2e2-1e8179bdb41f', + 'name': 'unspecified', + 'code': 'zzz', + 'source': 'rdacontent', + }, + { + 'id': '535e3160-763a-42f9-b0c0-d8ed7df6e2a2', + 'name': 'still image', + 'code': 'sti', + 'source': 'rdacontent', + }, + ], + query: {}, + parentResources: { + facets: { records: [] }, + }, + onFetchFacets: jest.fn(), + }; + + describe('when user opens a facet', () => { + it('should fetch only 6 options for this facet', () => { + useLocation.mockReturnValue({ search: '?segment=instances&sort=title' }); + + const { result } = renderHook(() => useFacets( + _segmentAccordions, + _segmentOptions, + _selectedFacetFilters, + _getNewRecords, + _data, + )); + + const onToggleSection = result.current[1]; + + act(() => { onToggleSection({ id: 'resource' }); }); + + expect(_data.onFetchFacets).toHaveBeenCalledWith({ facetToOpen: 'resource' }); + }); + }); + + describe('when user clicks the "+More" button under options', () => { + it('should fetch all options for this facet', () => { + const mockSetFacetSettings = jest.fn(); + useFacetSettings.mockReturnValue([{}, mockSetFacetSettings]); + + const { result } = renderHook(() => useFacets( + _segmentAccordions, + _segmentOptions, + _selectedFacetFilters, + _getNewRecords, + _data, + )); + + const handleFetchFacets = result.current[2]; + act(() => { handleFetchFacets({ onMoreClickedFacet: 'resource' }); }); + + expect(mockSetFacetSettings).toHaveBeenCalledWith('resource', { isOnMoreClicked: true }); + expect(_data.onFetchFacets).toHaveBeenCalledWith({ onMoreClickedFacet: 'resource' }); + }); + }); + + describe('when user places the cursor in the facet`s input field', () => { + it('should fetch all options for this facet', () => { + const { result } = renderHook(() => useFacets( + _segmentAccordions, + _segmentOptions, + _selectedFacetFilters, + _getNewRecords, + _data, + )); + + const handleFetchFacets = result.current[2]; + + act(() => { handleFetchFacets({ focusedFacet: 'resource' }); }); + + expect(_data.onFetchFacets).toHaveBeenCalledWith({ focusedFacet: 'resource' }); + }); + }); + + describe('when several facets are open and user enters a value in the search box', () => { + it('should fetch options for all open facets', () => { + useFacetSettings.mockReturnValue([{}, jest.fn()]); + useLocation.mockReturnValue({ search: '' }); + + const { result, rerender } = renderHook(({ newData }) => useFacets( + _segmentAccordions, + _segmentOptions, + _selectedFacetFilters, + _getNewRecords, + newData, + ), { + initialProps: { newData: _data }, + }); + + const onToggleSection = result.current[1]; + + act(() => { onToggleSection({ id: 'resource' }); }); + act(() => { onToggleSection({ id: 'format' }); }); + + _data.onFetchFacets.mockClear(); + + useLocation.mockReturnValue({ search: '?query=Mark' }); + + rerender({ + newData: { + ..._data, + query: { ..._data.query, query: 'Mark' }, + }, + }); + + expect(_data.onFetchFacets).toHaveBeenCalledWith({ + accordions: { resource: true, format: true }, + accordionsData: {}, + }); + }); + }); + + describe('when several facets are open and user selects an option of any facet', () => { + it('should fetch options for all open facets', () => { + useFacetSettings.mockReturnValue([{}, jest.fn()]); + useLocation.mockReturnValue({ search: '' }); + const isFetchFacetsAfterReset = false; + + const { result, rerender } = renderHook(({ newData, newSelectedFacetFilters }) => useFacets( + _segmentAccordions, + _segmentOptions, + newSelectedFacetFilters, + _getNewRecords, + newData, + isFetchFacetsAfterReset, + ), { + initialProps: { + newData: _data, + newSelectedFacetFilters: _selectedFacetFilters, + }, + }); + + const onToggleSection = result.current[1]; + + act(() => { onToggleSection({ id: 'resource' }); }); + act(() => { onToggleSection({ id: 'format' }); }); + + _data.onFetchFacets.mockClear(); + + useLocation.mockReturnValue({ search: '?callNumbersTenantId=university&qindex=callNumbers' }); + + rerender({ + newData: { + ..._data, + query: { ..._data.query, filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f' }, + }, + newSelectedFacetFilters: { ..._selectedFacetFilters, resource: ['6312d172-f0cf-40f6-b27d-9fa8feaf332f'] }, + }); + + expect(_data.onFetchFacets).toHaveBeenCalledWith({ + accordions: { resource: true, format: true }, + accordionsData: { + resource: { isSelected: true }, + }, + }); + }); + }); + + describe('when there is a value in the search box and any facet option is selected', () => { + describe('and the user selects another search option', () => { + it('should not call onFetchFacets', () => { + useFacetSettings.mockReturnValue([{}, jest.fn()]); + useLocation.mockReturnValue({ search: '?callNumbersTenantId=university&qindex=callNumbers&query=Mark' }); + const isFetchFacetsAfterReset = false; + + const { rerender } = renderHook(({ newData, newSelectedFacetFilters }) => useFacets( + _segmentAccordions, + _segmentOptions, + newSelectedFacetFilters, + _getNewRecords, + newData, + isFetchFacetsAfterReset, + ), { + initialProps: { + newData: { + ..._data, + query: { + ..._data.query, + query: 'Mark', + qindex: 'callNumbers', + filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f', + }, + }, + newSelectedFacetFilters: { ..._selectedFacetFilters, resource: ['6312d172-f0cf-40f6-b27d-9fa8feaf332f'] }, + }, + }); + + _data.onFetchFacets.mockClear(); + + useLocation.mockReturnValue(''); + + rerender({ + newData: { + ..._data, + query: { + ..._data.query, + query: undefined, + qindex: 'local', + filters: undefined, + }, + }, + newSelectedFacetFilters: _selectedFacetFilters, + }); + + expect(_data.onFetchFacets).not.toHaveBeenCalled(); + }); + }); + }); + + describe('when the "Search" lookup and there is a value in the search box and facet option is selected', () => { + describe('and the user reset the search', () => { + it('should call onFetchFacets for all open facets once', () => { + useFacetSettings.mockReturnValue([{}, jest.fn()]); + useLocation.mockReturnValue({ search: '?segment=instances&sort=title&query=Mark' }); + + const { result, rerender } = renderHook(({ newData, newSelectedFacetFilters }) => useFacets( + _segmentAccordions, + _segmentOptions, + newSelectedFacetFilters, + _getNewRecords, + newData, + ), { + initialProps: { + newData: { + ..._data, + query: { + ..._data.query, + query: 'Mark', + qindex: '', + filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f', + }, + }, + newSelectedFacetFilters: { ..._selectedFacetFilters, resource: ['6312d172-f0cf-40f6-b27d-9fa8feaf332f'] }, + }, + }); + + const onToggleSection = result.current[1]; + + act(() => { onToggleSection({ id: 'resource' }); }); + + _data.onFetchFacets.mockClear(); + + useLocation.mockReturnValue({ search: '' }); + + rerender({ + newData: { + ..._data, + query: { + ..._data.query, + query: undefined, + qindex: undefined, + filters: undefined, + }, + }, + newSelectedFacetFilters: _selectedFacetFilters, + }); + + expect(_data.onFetchFacets).toHaveBeenCalledTimes(1); + expect(_data.onFetchFacets).toHaveBeenCalledWith({ + accordions: { format: false, resource: true }, + accordionsData: expect.anything(), + }); + }); + }); + }); + + describe('when the "Browse" lookup and there is a value in the search box and facet option is selected', () => { + describe('and the user reset the search', () => { + it('should not call onFetchFacets', () => { + useFacetSettings.mockReturnValue([{}, jest.fn()]); + useLocation.mockReturnValue({ search: '?query=Mark' }); + const isFetchFacetsAfterReset = false; + + const { result, rerender } = renderHook(({ newData, newSelectedFacetFilters }) => useFacets( + _segmentAccordions, + _segmentOptions, + newSelectedFacetFilters, + _getNewRecords, + newData, + isFetchFacetsAfterReset, + ), { + initialProps: { + newData: { + ..._data, + query: { + ..._data.query, + query: 'Mark', + qindex: '', + filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f', + }, + }, + newSelectedFacetFilters: { ..._selectedFacetFilters, resource: ['6312d172-f0cf-40f6-b27d-9fa8feaf332f'] }, + }, + }); + + const onToggleSection = result.current[1]; + + act(() => { onToggleSection({ id: 'resource' }); }); + + _data.onFetchFacets.mockClear(); + + useLocation.mockReturnValue({ search: '' }); + + rerender({ + newData: { + ..._data, + query: { + ..._data.query, + query: undefined, + qindex: undefined, + filters: undefined, + }, + }, + newSelectedFacetFilters: _selectedFacetFilters, + }); + + expect(_data.onFetchFacets).not.toHaveBeenCalled(); + }); + }); + }); + }); + it('returns initial state', () => { const { result } = renderHook(() => useFacets( segmentAccordions, diff --git a/src/components/BrowseInventoryFilters/BrowseInventoryFilters.js b/src/components/BrowseInventoryFilters/BrowseInventoryFilters.js index 55ad1ad28..d0e40898c 100644 --- a/src/components/BrowseInventoryFilters/BrowseInventoryFilters.js +++ b/src/components/BrowseInventoryFilters/BrowseInventoryFilters.js @@ -19,14 +19,16 @@ const BrowseInventoryFilters = ({ const data = useContext(DataContext); const filters = omit(activeFilters || {}, ['qindex', 'query']); + const isBrowseLookup = true; const filtersData = { ...data, browseType: searchIndex, - onFetchFacets: fetchFacets(data), + onFetchFacets: fetchFacets(data, isBrowseLookup), parentResources: resources, query: { query: activeFilters.query, filters: parseFiltersToStr(filters), + qindex: searchIndex, } }; diff --git a/src/components/CheckboxFacet/CheckboxFacet.js b/src/components/CheckboxFacet/CheckboxFacet.js index 818a55f9a..aa169ecfc 100644 --- a/src/components/CheckboxFacet/CheckboxFacet.js +++ b/src/components/CheckboxFacet/CheckboxFacet.js @@ -49,10 +49,6 @@ export default class CheckboxFacet extends React.Component { ) { this.updateMore(); } - - if (prevDataLength > currentDataLength && currentDataLength === DEFAULT_FILTERS_NUMBER) { - this.setDefaultMore(); - } } onMoreClick = (totalOptions) => { @@ -105,10 +101,6 @@ export default class CheckboxFacet extends React.Component { }); } - setDefaultMore = () => { - this.setState(({ more: SHOW_OPTIONS_COUNT })); - } - render() { const { dataOptions, diff --git a/src/components/InstanceFilters/InstanceFiltersBrowse/InstanceFiltersBrowse.js b/src/components/InstanceFilters/InstanceFiltersBrowse/InstanceFiltersBrowse.js index edfefba59..1fa52e953 100644 --- a/src/components/InstanceFilters/InstanceFiltersBrowse/InstanceFiltersBrowse.js +++ b/src/components/InstanceFilters/InstanceFiltersBrowse/InstanceFiltersBrowse.js @@ -113,16 +113,22 @@ const InstanceFiltersBrowse = props => { handleFilterSearch, facetsOptions, getIsPending, + onUnregisterAccordion, ] = useFacets( segmentAccordions, segmentOptions, selectedFacetFilters, getNewRecords, - props.data + props.data, + false, ); return ( - + {Object.values(browseCallNumberOptions).includes(browseType) && ( <> {isUserInMemberTenant && ( diff --git a/src/components/NewOrderModal/NewOrderModalContainer.js b/src/components/NewOrderModal/NewOrderModalContainer.js index 2266554a3..71f527dd4 100644 --- a/src/components/NewOrderModal/NewOrderModalContainer.js +++ b/src/components/NewOrderModal/NewOrderModalContainer.js @@ -9,8 +9,12 @@ import { useParams, } from 'react-router-dom'; -import { useOkapiKy } from '@folio/stripes/core'; +import { + useOkapiKy, + useStripes, +} from '@folio/stripes/core'; +import { useInstance } from '../../common'; import { ORDERS_API } from '../../constants'; import NewOrderModal from './NewOrderModal'; @@ -20,7 +24,9 @@ const NewOrderModalContainer = ({ }) => { const history = useHistory(); const ky = useOkapiKy(); + const stripes = useStripes(); const { id } = useParams(); + const { instance } = useInstance(id); const [orderId, setOrderId] = useState(); const validatePONumber = useCallback(async (poNumber) => { @@ -45,9 +51,10 @@ const NewOrderModalContainer = ({ const onSubmit = useCallback(() => { const route = orderId ? `/orders/view/${orderId}/po-line/create` : '/orders/create'; + const instanceTenantId = instance?.tenantId || stripes.okapi.tenant; - history.push(route, { instanceId: id }); - }, [orderId]); + history.push(route, { instanceId: instance?.id, instanceTenantId }); + }, [instance, orderId, stripes.okapi.tenant]); return ( ({ ...jest.requireActual('react-router-dom'), useHistory: jest.fn() })); +jest.mock('../../common', () => ({ + ...jest.requireActual('../../common'), + useInstance: jest.fn(), +})); + +global.document.createRange = jest.fn(() => new Range()); const defaultProps = { onCancel: jest.fn(), @@ -44,18 +51,25 @@ describe('NewOrderModalContainer', () => { const historyMock = { push: jest.fn(), }; + const order = { + id: 'orderId', + }; const kyMock = { get: jest.fn(() => ({ json: () => Promise.resolve({ - purchaseOrders: [{ id: 'orderId' }], + purchaseOrders: [order], }) })) }; beforeEach(() => { + historyMock.push.mockClear(); useHistory .mockClear() .mockReturnValue(historyMock); + useInstance + .mockClear() + .mockReturnValue({ instance }); useOkapiKy .mockClear() .mockReturnValue(kyMock); @@ -70,7 +84,7 @@ describe('NewOrderModalContainer', () => { it('should navigate to \'PO\' creation form when create btn was clicked and \'PO number\' field is empty', async () => { renderNewOrderModalContainer(); - await act(async () => fireEvent.click(screen.getByText(/^Create$/))); + await userEvent.click(screen.getByText(/^Create$/)); expect(historyMock.push).toHaveBeenCalledWith('/orders/create', expect.anything()); }); @@ -78,9 +92,19 @@ describe('NewOrderModalContainer', () => { it('should navigate to \'PO Line\' creation form when create btn was clicked and PO is exist', async () => { renderNewOrderModalContainer(); - await act(async () => fireEvent.change(screen.getByLabelText(/PO number/i), { target: { value: '123' } })); - await act(async () => fireEvent.click(screen.getByText(/^Create$/))); + await userEvent.type(screen.getByLabelText(/PO number/i), '123'); + await userEvent.tab(); + + expect(screen.queryByText('ui-inventory.newOrder.modal.PONumber.doesNotExist')).not.toBeInTheDocument(); + + await userEvent.click(await screen.findByText(/^Create$/)); - expect(historyMock.push).toHaveBeenCalledWith('/orders/create', { 'instanceId': undefined }); + expect(historyMock.push).toHaveBeenCalledWith( + `/orders/view/${order.id}/po-line/create`, + { + instanceId: instance.id, + instanceTenantId: 'diku', + }, + ); }); }); diff --git a/src/constants.js b/src/constants.js index f29ecd6f6..534d4d280 100644 --- a/src/constants.js +++ b/src/constants.js @@ -372,50 +372,6 @@ export const FACETS_TO_REQUEST = { [FACETS.HOLDINGS_TYPE]: FACETS_CQL.HOLDINGS_TYPE, }; -const INSTANCES_FACET_ENDPOINT = 'search/instances/facets'; -const CONTRIBUTORS_FACET_ENDPOINT = 'search/contributors/facets'; -const SUBJECTS_FACET_ENDPOINT = 'search/subjects/facets'; - -export const FACETS_ENDPOINTS = { - [FACETS.SHARED]: INSTANCES_FACET_ENDPOINT, - [FACETS.CONTRIBUTORS_SHARED]: CONTRIBUTORS_FACET_ENDPOINT, - [FACETS.SUBJECTS_SHARED]: SUBJECTS_FACET_ENDPOINT, - [FACETS.HELD_BY]: INSTANCES_FACET_ENDPOINT, - [FACETS.CALL_NUMBERS_HELD_BY]: INSTANCES_FACET_ENDPOINT, - [FACETS.CONTRIBUTORS_HELD_BY]: CONTRIBUTORS_FACET_ENDPOINT, - [FACETS.SUBJECTS_HELD_BY]: SUBJECTS_FACET_ENDPOINT, - [FACETS.EFFECTIVE_LOCATION]: INSTANCES_FACET_ENDPOINT, - [FACETS.LANGUAGE]: INSTANCES_FACET_ENDPOINT, - [FACETS.RESOURCE]: INSTANCES_FACET_ENDPOINT, - [FACETS.FORMAT]: INSTANCES_FACET_ENDPOINT, - [FACETS.MODE]: INSTANCES_FACET_ENDPOINT, - [FACETS.NATURE_OF_CONTENT]: INSTANCES_FACET_ENDPOINT, - [FACETS.STAFF_SUPPRESS]: INSTANCES_FACET_ENDPOINT, - [FACETS.INSTANCES_DISCOVERY_SUPPRESS]: INSTANCES_FACET_ENDPOINT, - [FACETS.HOLDINGS_DISCOVERY_SUPPRESS]: INSTANCES_FACET_ENDPOINT, - [FACETS.ITEMS_DISCOVERY_SUPPRESS]: INSTANCES_FACET_ENDPOINT, - [FACETS.SOURCE]: INSTANCES_FACET_ENDPOINT, - [FACETS.STATUS]: INSTANCES_FACET_ENDPOINT, - [FACETS.INSTANCES_TAGS]: INSTANCES_FACET_ENDPOINT, - [FACETS.ITEMS_TAGS]: INSTANCES_FACET_ENDPOINT, - [FACETS.HOLDINGS_TAGS]: INSTANCES_FACET_ENDPOINT, - [FACETS.MATERIAL_TYPE]: INSTANCES_FACET_ENDPOINT, - [FACETS.ITEM_STATUS]: INSTANCES_FACET_ENDPOINT, - [FACETS.HOLDINGS_PERMANENT_LOCATION]: INSTANCES_FACET_ENDPOINT, - [FACETS.CREATED_DATE]: INSTANCES_FACET_ENDPOINT, - [FACETS.UPDATED_DATE]: INSTANCES_FACET_ENDPOINT, - [FACETS.HOLDINGS_CREATED_DATE]: INSTANCES_FACET_ENDPOINT, - [FACETS.HOLDINGS_UPDATED_DATE]: INSTANCES_FACET_ENDPOINT, - [FACETS.HOLDINGS_SOURCE]: INSTANCES_FACET_ENDPOINT, - [FACETS.ITEMS_CREATED_DATE]: INSTANCES_FACET_ENDPOINT, - [FACETS.ITEMS_UPDATED_DATE]: INSTANCES_FACET_ENDPOINT, - [FACETS.STATISTICAL_CODE_IDS]: INSTANCES_FACET_ENDPOINT, - [FACETS.HOLDINGS_STATISTICAL_CODE_IDS]: INSTANCES_FACET_ENDPOINT, - [FACETS.ITEMS_STATISTICAL_CODE_IDS]: INSTANCES_FACET_ENDPOINT, - [FACETS.NAME_TYPE]: CONTRIBUTORS_FACET_ENDPOINT, - [FACETS.HOLDINGS_TYPE]: INSTANCES_FACET_ENDPOINT, -}; - export const FACETS_OPTIONS = { SHARED_OPTIONS: 'sharedOptions', HELD_BY_OPTIONS: 'heldByOptions', @@ -507,17 +463,17 @@ export const SINGLE_ITEM_QUERY_TEMPLATES = { export const fieldSearchConfigurations = { keyword: { - exactPhrase: 'keyword==/string "%{query.query}"', + exactPhrase: 'keyword=="%{query.query}"', containsAll: 'keyword all "%{query.query}"', startsWith: 'keyword all "%{query.query}*"', }, contributor: { - exactPhrase: 'contributors.name==/string "%{query.query}"', + exactPhrase: 'contributors.name=="%{query.query}"', containsAll: 'contributors.name="*%{query.query}*"', startsWith: 'contributors.name="%{query.query}*"', }, title: { - exactPhrase: 'title==/string "%{query.query}"', + exactPhrase: 'title=="%{query.query}"', containsAll: 'title all "%{query.query}"', startsWith: 'title all "%{query.query}*"', }, @@ -542,24 +498,24 @@ export const fieldSearchConfigurations = { startsWith: 'oclc="%{query.query}*"', }, instanceNotes: { - exactPhrase: 'notes.note==/string "%{query.query}" or administrativeNotes==/string "%{query.query}"', + exactPhrase: 'notes.note=="%{query.query}" or administrativeNotes=="%{query.query}"', containsAll: 'notes.note all "%{query.query}" or administrativeNotes all "%{query.query}"', startsWith: 'notes.note all "%{query.query}*" or administrativeNotes all "%{query.query}*"', }, instanceAdministrativeNotes: { - exactPhrase: 'administrativeNotes==/string "%{query.query}"', + exactPhrase: 'administrativeNotes=="%{query.query}"', containsAll: 'administrativeNotes all "%{query.query}"', startsWith: 'administrativeNotes all "%{query.query}*"', }, subject: { - exactPhrase: 'subjects.value==/string "%{query.query}"', + exactPhrase: 'subjects.value=="%{query.query}"', containsAll: 'subjects.value all "%{query.query}"', - startsWith: 'subjects.value==/string "%{query.query}*"', + startsWith: 'subjects.value=="%{query.query}*"', }, callNumber: { - exactPhrase: 'itemEffectiveShelvingOrder==/string "%{query.query}"', + exactPhrase: 'itemEffectiveShelvingOrder=="%{query.query}"', containsAll: 'itemEffectiveShelvingOrder="*%{query.query}*"', - startsWith: 'itemEffectiveShelvingOrder==/string "%{query.query}*"', + startsWith: 'itemEffectiveShelvingOrder=="%{query.query}*"', }, hrid: { exactPhrase: 'hrid=="%{query.query}"', @@ -577,7 +533,7 @@ export const fieldSearchConfigurations = { startsWith: 'authorityId=="%{query.query}*"', }, allFields: { - exactPhrase: 'cql.all==/string "%{query.query}"', + exactPhrase: 'cql.all=="%{query.query}"', containsAll: 'cql.all all "%{query.query}"', startsWith: 'cql.all all "%{query.query}*"', }, @@ -592,12 +548,12 @@ export const fieldSearchConfigurations = { startsWith: 'holdingsNormalizedCallNumbers="%{query.query}*"', }, holdingsNotes: { - exactPhrase: 'holdings.notes.note==/string "%{query.query}" or holdings.administrativeNotes==/string "%{query.query}"', + exactPhrase: 'holdings.notes.note=="%{query.query}" or holdings.administrativeNotes=="%{query.query}"', containsAll: 'holdings.notes.note all "%{query.query}" or holdings.administrativeNotes all "%{query.query}"', startsWith: 'holdings.notes.note all "%{query.query}*" or holdings.administrativeNotes all "%{query.query}*"', }, holdingsAdministrativeNotes: { - exactPhrase: 'holdings.administrativeNotes==/string "%{query.query}"', + exactPhrase: 'holdings.administrativeNotes=="%{query.query}"', containsAll: 'holdings.administrativeNotes all "%{query.query}"', startsWith: 'holdings.administrativeNotes all "%{query.query}*"', }, @@ -627,17 +583,17 @@ export const fieldSearchConfigurations = { startsWith: 'itemNormalizedCallNumbers="%{query.query}*"', }, itemNotes: { - exactPhrase: 'item.notes.note==/string "%{query.query}" or item.administrativeNotes==/string "%{query.query}"', + exactPhrase: 'item.notes.note=="%{query.query}" or item.administrativeNotes=="%{query.query}"', containsAll: 'item.notes.note all "%{query.query}" or item.administrativeNotes all "%{query.query}"', startsWith: 'item.notes.note all "%{query.query}*" or item.administrativeNotes all "%{query.query}*"', }, itemAdministrativeNotes: { - exactPhrase: 'item.administrativeNotes==/string "%{query.query}"', + exactPhrase: 'item.administrativeNotes=="%{query.query}"', containsAll: 'item.administrativeNotes all "%{query.query}"', startsWith: 'item.administrativeNotes all "%{query.query}*"', }, itemCirculationNotes: { - exactPhrase: 'item.circulationNotes.note==/string "%{query.query}"', + exactPhrase: 'item.circulationNotes.note=="%{query.query}"', containsAll: 'item.circulationNotes.note all "%{query.query}"', startsWith: 'item.circulationNotes.note all "%{query.query}*"', }, @@ -679,3 +635,16 @@ export const OKAPI_TOKEN_HEADER = 'X-Okapi-Token'; export const CONTENT_TYPE_HEADER = 'Content-Type'; export const DEFAULT_ITEM_TABLE_SORTBY_FIELD = 'barcode'; + +export const AUTHORITY_LINKED_FIELDS = [ + 'alternativeTitles', + 'contributors', + 'subjects', + 'series', +]; + +export const INSTANCE_SHARING_STATUSES = { + COMPLETE: 'COMPLETE', + ERROR: 'ERROR', + IN_PROGRESS: 'IN_PROGRESS', +}; diff --git a/src/routes/buildManifestObject.test.js b/src/routes/buildManifestObject.test.js index 00867bbae..877c01783 100644 --- a/src/routes/buildManifestObject.test.js +++ b/src/routes/buildManifestObject.test.js @@ -79,7 +79,7 @@ describe('buildQuery', () => { const cql = buildQuery(...getBuildQueryArgs({ queryParams })); expect(cql).toContain( - '(keyword all "test" or title==/string "hello")' + '(keyword all "test" or title=="hello")' ); }); diff --git a/src/settings/CallNumberTypes.js b/src/settings/CallNumberTypes.js index a2488588a..351e590a9 100644 --- a/src/settings/CallNumberTypes.js +++ b/src/settings/CallNumberTypes.js @@ -23,8 +23,8 @@ class CallNumberTypes extends React.Component { } actionSuppressor = { - edit: getSourceSuppressor(RECORD_SOURCE.SYSTEM), - delete: getSourceSuppressor(RECORD_SOURCE.SYSTEM), + edit: getSourceSuppressor([RECORD_SOURCE.SYSTEM, RECORD_SOURCE.CONSORTIUM]), + delete: getSourceSuppressor([RECORD_SOURCE.SYSTEM, RECORD_SOURCE.CONSORTIUM]), } render() { diff --git a/src/settings/CallNumberTypes.test.js b/src/settings/CallNumberTypes.test.js index 82ea0f527..99ab55675 100644 --- a/src/settings/CallNumberTypes.test.js +++ b/src/settings/CallNumberTypes.test.js @@ -45,8 +45,8 @@ describe('CallNumberTypes', () => { renderCallNumberTypes(); const actionSuppressor = { - delete: expect(getSourceSuppressor).toHaveBeenNthCalledWith(1, RECORD_SOURCE.SYSTEM), - edit: expect(getSourceSuppressor).toHaveBeenNthCalledWith(2, RECORD_SOURCE.SYSTEM), + delete: expect(getSourceSuppressor).toHaveBeenNthCalledWith(1, [RECORD_SOURCE.SYSTEM, RECORD_SOURCE.CONSORTIUM]), + edit: expect(getSourceSuppressor).toHaveBeenNthCalledWith(2, [RECORD_SOURCE.SYSTEM, RECORD_SOURCE.CONSORTIUM]), }; expect(ControlledVocab).toHaveBeenCalledWith(expect.objectContaining({ actionSuppressor }), {}); diff --git a/src/withFacets.js b/src/withFacets.js index 85123e20d..1675d2fcf 100644 --- a/src/withFacets.js +++ b/src/withFacets.js @@ -1,8 +1,12 @@ -import { reduce } from 'lodash'; +import { + reduce, + omit, +} from 'lodash'; import React from 'react'; import PropTypes from 'prop-types'; import { makeQueryFunction } from '@folio/stripes/smart-components'; +import { buildFilterQuery } from '@folio/stripes-acq-components'; import { getQueryTemplate, @@ -13,9 +17,7 @@ import { } from './filterConfig'; import { DEFAULT_FILTERS_NUMBER, - FACETS, FACETS_TO_REQUEST, - FACETS_ENDPOINTS, CQL_FIND_ALL, browseModeOptions, browseModeMap, @@ -114,7 +116,62 @@ function withFacets(WrappedComponent) { }, ''); }; - fetchFacets = (data) => async (properties = {}) => { + getEndpoint = (queryIndex) => { + if (queryIndex === browseModeOptions.CONTRIBUTORS) { + return 'search/contributors/facets'; + } + + if (queryIndex === browseModeOptions.SUBJECTS) { + return 'search/subjects/facets'; + } + + return 'search/instances/facets'; + } + + getCqlQuery = (isBrowseLookup, query, queryIndex, data) => { + if (isBrowseLookup) { + const normalizedFilters = { + ...Object.entries(query).reduce((acc, [key, value]) => ({ + ...acc, + [FACETS_TO_REQUEST[key] || key]: value, + }), {}), + query: query.query || undefined, + }; + + const otherFilters = omit(normalizedFilters, 'query', 'qindex'); + const hasSelectedFacetOption = Object.values(otherFilters).some(Boolean); + + let queryForBrowseFacets = ''; + + const isTypedCallNumber = Object.values(browseCallNumberOptions).includes(queryIndex) + && queryIndex !== browseCallNumberOptions.CALL_NUMBERS; + + if (hasSelectedFacetOption) { + if (isTypedCallNumber) { + queryForBrowseFacets = `callNumberType="${queryIndex}"`; + } + } else if (isTypedCallNumber) { + queryForBrowseFacets = `callNumberType="${queryIndex}"`; + } else { + queryForBrowseFacets = 'cql.allRecords=1'; + } + + return buildFilterQuery( + { + query: queryForBrowseFacets, + qindex: normalizedFilters.qindex, + ...otherFilters, + }, + _query => _query, + undefined, + false, + ); + } + + return buildQuery(query, {}, { ...data, query }, { log: () => null }) || ''; + } + + fetchFacets = (data, isBrowseLookup) => async (properties = {}) => { const { onMoreClickedFacet, focusedFacet, @@ -136,24 +193,13 @@ function withFacets(WrappedComponent) { // temporary query value const params = { query: 'id = *' }; - const cqlQuery = buildQuery(query, {}, { ...data, query }, { log: () => null }) || ''; const facetName = facetToOpen || onMoreClickedFacet || focusedFacet; const facetNameToRequest = FACETS_TO_REQUEST[facetName]; const paramsUrl = new URLSearchParams(window.location.search); const queryIndex = paramsUrl.get('qindex') || query?.qindex; + const cqlQuery = this.getCqlQuery(isBrowseLookup, query, queryIndex, data); - if (facetName === FACETS.NAME_TYPE) { - params.query = 'contributorNameTypeId=*'; - } else if ([FACETS.CONTRIBUTORS_SHARED, FACETS.CONTRIBUTORS_HELD_BY].includes(facetName)) { - params.query = 'name=*'; - } else if ([FACETS.SUBJECTS_SHARED, FACETS.SUBJECTS_HELD_BY].includes(facetName)) { - params.query = 'value=*'; - } else if (cqlQuery - && Object.values(browseCallNumberOptions).includes(queryIndex) - && queryIndex !== browseCallNumberOptions.CALL_NUMBERS - ) { - params.query = `callNumberType="${queryIndex}"`; - } else if (cqlQuery && queryIndex !== browseModeOptions.CALL_NUMBERS) { + if (cqlQuery) { params.query = cqlQuery; } @@ -174,7 +220,7 @@ function withFacets(WrappedComponent) { try { reset(); - const requestPath = FACETS_ENDPOINTS[facetName] || 'search/instances/facets'; + const requestPath = this.getEndpoint(queryIndex); await GET({ path: requestPath, params }); } catch (error) { throw new Error(error); diff --git a/src/withFacets.test.js b/src/withFacets.test.js index 3fe138e1d..b8c789e99 100644 --- a/src/withFacets.test.js +++ b/src/withFacets.test.js @@ -9,17 +9,19 @@ import { queryIndexes, FACETS_CQL, browseCallNumberOptions, + browseModeOptions, } from './constants'; const WrappedComponent = ({ fetchFacets, data = {}, properties = {}, + isBrowseLookup, }) => { return ( @@ -36,12 +38,392 @@ const mutator = { }; describe('withFacets', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + describe('when opening a contributots shared facet', () => { - it('should make a request with correct params', () => { + describe('and there are no selected options in other facets', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseModeOptions.CONTRIBUTORS, + query: '', + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.INSTANCES_SHARED}:6`, + query: '(cql.allRecords=1)', + }, + path: 'search/contributors/facets', + })); + }); + }); + + describe('and there is a selected option in another facet', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseModeOptions.CONTRIBUTORS, + query: '', + contributorsTenantId: ['college'], + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.INSTANCES_SHARED}:6`, + query: 'instances.tenantId==("college")', + }, + path: 'search/contributors/facets', + })); + }); + }); + + describe('and there is a selected option in another facet and there is a value in the search box', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseModeOptions.CONTRIBUTORS, + query: 'Marc Twain', + contributorsTenantId: ['college'], + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.INSTANCES_SHARED}:6`, + query: 'instances.tenantId==("college")', + }, + path: 'search/contributors/facets', + })); + }); + }); + }); + + describe('when opening a subjects shared facet', () => { + describe('and there are no selected options in other facets', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseModeOptions.SUBJECTS, + query: '', + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.INSTANCES_SHARED}:6`, + query: '(cql.allRecords=1)', + }, + path: 'search/subjects/facets', + })); + }); + }); + + describe('and there is a selected option in another facet', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseModeOptions.SUBJECTS, + query: '', + contributorsTenantId: ['college'], + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.INSTANCES_SHARED}:6`, + query: 'instances.tenantId==("college")', + }, + path: 'search/subjects/facets', + })); + }); + }); + + describe('and there is a selected option in another facet and there is a value in the search box', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseModeOptions.SUBJECTS, + query: 'Marc Twain', + contributorsTenantId: ['college'], + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.INSTANCES_SHARED}:6`, + query: 'instances.tenantId==("college")', + }, + path: 'search/subjects/facets', + })); + }); + }); + }); + + describe('when opening call numbers browse shared facet', () => { + describe('and there are no selected options in other facets', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseCallNumberOptions.CALL_NUMBERS, + query: '', + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.SHARED}:6`, + query: '(cql.allRecords=1)', + }, + path: 'search/instances/facets', + })); + }); + }); + + describe('and there is a selected option in another facet', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseCallNumberOptions.CALL_NUMBERS, + query: '', + contributorsTenantId: ['college'], + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.SHARED}:6`, + query: 'instances.tenantId==("college")', + }, + path: 'search/instances/facets', + })); + }); + }); + + describe('and there is a selected option in another facet and there is a value in the search box', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseCallNumberOptions.CALL_NUMBERS, + query: 'Marc Twain', + contributorsTenantId: ['college'], + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.SHARED}:6`, + query: 'instances.tenantId==("college")', + }, + path: 'search/instances/facets', + })); + }); + }); + }); + + describe('when opening call numbers sub-type browse shared facet', () => { + describe('and there are no selected options in other facets', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseCallNumberOptions.DEWEY, + query: '', + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.SHARED}:6`, + query: '(callNumberType="dewey")', + }, + path: 'search/instances/facets', + })); + }); + }); + + describe('and there is a selected option in another facet', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseCallNumberOptions.DEWEY, + query: '', + contributorsTenantId: ['college'], + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.SHARED}:6`, + query: '(callNumberType="dewey") and instances.tenantId==("college")', + }, + path: 'search/instances/facets', + })); + }); + }); + + describe('and there is a selected option in another facet and there is a value in the search box', () => { + it('should make a request with correct request options', () => { + const resources = { + query: { + qindex: browseCallNumberOptions.DEWEY, + query: 'Marc Twain', + contributorsTenantId: ['college'], + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.SHARED}:6`, + query: '(callNumberType="dewey") and instances.tenantId==("college")', + }, + path: 'search/instances/facets', + })); + }); + }); + }); + + describe('when Advanced search is used', () => { + it('should fetch facets with the correct params', async () => { const resources = { query: { - qindex: queryIndexes.CONTRIBUTOR, - query: '', + qindex: queryIndexes.ADVANCED_SEARCH, + query: 'isbn containsAll test1 or title exactPhrase test2 or keyword startsWith test3', }, }; @@ -49,7 +431,7 @@ describe('withFacets', () => { ); @@ -57,27 +439,24 @@ describe('withFacets', () => { expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ params: { - facet: `${FACETS_CQL.INSTANCES_SHARED}:6`, - query: 'name=*', + facet: 'source:6', + query: 'isbn="*test1*" or title=="test2" or keyword all "test3*"', }, })); }); }); - describe('when opening a subjects shared facet', () => { - it('should make a request with correct params', () => { + describe('when user opens a facet', () => { + it('should fetch only 6 options for this facet', () => { const resources = { - query: { - qindex: queryIndexes.SUBJECT, - query: '', - }, + query: {}, }; const { getByText } = render( ); @@ -85,27 +464,51 @@ describe('withFacets', () => { expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ params: { - facet: `${FACETS_CQL.INSTANCES_SHARED}:6`, - query: 'value=*', + facet: `${FACETS_CQL.SHARED}:6`, + query: 'id = *', }, + path: 'search/instances/facets', })); }); }); - describe('when using call numbers browse', () => { - it('should make a request with correct params', () => { + describe('when user clicks the "+More" button under options', () => { + it('should fetch all options for this facet', () => { const resources = { - query: { - qindex: browseCallNumberOptions.CALL_NUMBERS, - query: 'test', + query: {}, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.INSTANCE_TYPE}`, + query: 'id = *', }, + path: 'search/instances/facets', + })); + }); + }); + + describe('when user places the cursor in the facet`s input field', () => { + it('should fetch all options for this facet', () => { + const resources = { + query: {}, }; const { getByText } = render( ); @@ -113,27 +516,33 @@ describe('withFacets', () => { expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ params: { - facet: `${FACETS_CQL.SHARED}:6`, + facet: `${FACETS_CQL.INSTANCE_TYPE}`, query: 'id = *', }, + path: 'search/instances/facets', })); }); }); - describe('when using call numbers sub-type browse', () => { - it('should make a request with correct params', () => { + describe('when several facets are open and user enters a value in the search box', () => { + it('should fetch options for all open facets', () => { const resources = { query: { - qindex: browseCallNumberOptions.DEWEY, - query: 'test', + qindex: 'title', + query: 'Marc', }, }; + const properties = { + accordions: { resource: true, format: true }, + accordionsData: {}, + }; + const { getByText } = render( ); @@ -141,19 +550,27 @@ describe('withFacets', () => { expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ params: { - facet: `${FACETS_CQL.SHARED}:6`, - query: 'callNumberType="dewey"', + facet: `${FACETS_CQL.INSTANCE_TYPE}:6,${FACETS_CQL.INSTANCE_FORMAT}:6`, + query: 'title all "Marc"', }, + path: 'search/instances/facets', })); }); }); - describe('when Advanced search is used', () => { - it('should fetch facets with the correct params', async () => { + describe('when several facets are open and user selects an option of any facet', () => { + it('should fetch options for all open facets', () => { const resources = { query: { - qindex: queryIndexes.ADVANCED_SEARCH, - query: 'isbn containsAll test1 or title exactPhrase test2 or keyword startsWith test3', + query: '', + filters: 'resource.6312d172-f0cf-40f6-b27d-9fa8feaf332f', + }, + }; + + const properties = { + accordions: { resource: true, format: true }, + accordionsData: { + resource: { isSelected: true }, }, }; @@ -161,7 +578,7 @@ describe('withFacets', () => { ); @@ -169,10 +586,49 @@ describe('withFacets', () => { expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ params: { - facet: 'source:6', - query: 'isbn="*test1*" or title==/string "test2" or keyword all "test3*"', + facet: `${FACETS_CQL.INSTANCE_TYPE},${FACETS_CQL.INSTANCE_FORMAT}:6`, + query: 'instanceTypeId=="6312d172-f0cf-40f6-b27d-9fa8feaf332f"', }, + path: 'search/instances/facets', })); }); }); + + describe('when several facets are open and user selects an option of one facet', () => { + describe('and for another one the "+More" button is clicked', () => { + it('should fetch all options for the facet with selected option and for the facet with +More clicked', () => { + const resources = { + query: { + query: '', + filters: 'format.8d511d33-5e85-4c5d-9bce-6e3c9cd0c324', + }, + }; + + const properties = { + accordions: { shared: true, resource: true, format: true }, + accordionsData: { + resource: { isOnMoreClicked: true }, + format: { isSelected: true }, + }, + }; + + const { getByText } = render( + + ); + + fireEvent.click(getByText('fetchFacetsButton')); + + expect(mutator.facets.GET).toHaveBeenCalledWith(expect.objectContaining({ + params: { + facet: `${FACETS_CQL.SHARED}:6,${FACETS_CQL.INSTANCE_TYPE},${FACETS_CQL.INSTANCE_FORMAT}`, + query: expect.anything(), + }, + })); + }); + }); + }); }); diff --git a/translations/ui-inventory/ar.json b/translations/ui-inventory/ar.json index 7965045e0..161a0e0b9 100644 --- a/translations/ui-inventory/ar.json +++ b/translations/ui-inventory/ar.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/ber.json b/translations/ui-inventory/ber.json index 55d6107eb..7350fadaa 100644 --- a/translations/ui-inventory/ber.json +++ b/translations/ui-inventory/ber.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/ca.json b/translations/ui-inventory/ca.json index 2045ca5d8..8383b2062 100644 --- a/translations/ui-inventory/ca.json +++ b/translations/ui-inventory/ca.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/cs_CZ.json b/translations/ui-inventory/cs_CZ.json index 60fcfc09a..7c5f19a7e 100644 --- a/translations/ui-inventory/cs_CZ.json +++ b/translations/ui-inventory/cs_CZ.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/da.json b/translations/ui-inventory/da.json index b49a45c57..045ab062d 100644 --- a/translations/ui-inventory/da.json +++ b/translations/ui-inventory/da.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/de.json b/translations/ui-inventory/de.json index f2a744f91..6c1959203 100644 --- a/translations/ui-inventory/de.json +++ b/translations/ui-inventory/de.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/en.json b/translations/ui-inventory/en.json index 4678d740c..afa026c97 100644 --- a/translations/ui-inventory/en.json +++ b/translations/ui-inventory/en.json @@ -54,6 +54,9 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", + "unlinkLocalMarcAuthorities.modal.header": "Linked to local authorities", + "unlinkLocalMarcAuthorities.modal.message": "If you proceed with sharing this instance, then {linkedAuthoritiesLength} bibliographic fields linked to local authority records will retain authorized values but will become uncontrolled", + "unlinkLocalMarcAuthorities.modal.proceed": "Proceed", "save": "Save", "cancel": "Cancel", "confirm": "Confirm", diff --git a/translations/ui-inventory/en_GB.json b/translations/ui-inventory/en_GB.json index 55d6107eb..7350fadaa 100644 --- a/translations/ui-inventory/en_GB.json +++ b/translations/ui-inventory/en_GB.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/en_SE.json b/translations/ui-inventory/en_SE.json index 55d6107eb..7350fadaa 100644 --- a/translations/ui-inventory/en_SE.json +++ b/translations/ui-inventory/en_SE.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/en_US.json b/translations/ui-inventory/en_US.json index 55d6107eb..7350fadaa 100644 --- a/translations/ui-inventory/en_US.json +++ b/translations/ui-inventory/en_US.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/es.json b/translations/ui-inventory/es.json index 9441746ac..abff5b97e 100644 --- a/translations/ui-inventory/es.json +++ b/translations/ui-inventory/es.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/es_419.json b/translations/ui-inventory/es_419.json index 3210353a8..a3fc24001 100644 --- a/translations/ui-inventory/es_419.json +++ b/translations/ui-inventory/es_419.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Compartir", "shareLocalInstance.toast.unsuccessful": "La instancia local {instanceTitle} no se compartió", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/es_ES.json b/translations/ui-inventory/es_ES.json index 213e05c2e..42099fcb0 100644 --- a/translations/ui-inventory/es_ES.json +++ b/translations/ui-inventory/es_ES.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/fr.json b/translations/ui-inventory/fr.json index eeab84841..292eec37e 100644 --- a/translations/ui-inventory/fr.json +++ b/translations/ui-inventory/fr.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/fr_FR.json b/translations/ui-inventory/fr_FR.json index f8af0420d..02ab60902 100644 --- a/translations/ui-inventory/fr_FR.json +++ b/translations/ui-inventory/fr_FR.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/he.json b/translations/ui-inventory/he.json index c8c889012..32e4e2273 100644 --- a/translations/ui-inventory/he.json +++ b/translations/ui-inventory/he.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/hi_IN.json b/translations/ui-inventory/hi_IN.json index 55d6107eb..7350fadaa 100644 --- a/translations/ui-inventory/hi_IN.json +++ b/translations/ui-inventory/hi_IN.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/hu.json b/translations/ui-inventory/hu.json index 6ed0c9a9b..66785515d 100644 --- a/translations/ui-inventory/hu.json +++ b/translations/ui-inventory/hu.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/it_IT.json b/translations/ui-inventory/it_IT.json index c24e9c043..ebcb79611 100644 --- a/translations/ui-inventory/it_IT.json +++ b/translations/ui-inventory/it_IT.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/ja.json b/translations/ui-inventory/ja.json index 27bbe4214..0761df8f7 100644 --- a/translations/ui-inventory/ja.json +++ b/translations/ui-inventory/ja.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/ko.json b/translations/ui-inventory/ko.json index 671176c77..d396b651f 100644 --- a/translations/ui-inventory/ko.json +++ b/translations/ui-inventory/ko.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/nb.json b/translations/ui-inventory/nb.json index 55d6107eb..7350fadaa 100644 --- a/translations/ui-inventory/nb.json +++ b/translations/ui-inventory/nb.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/nn.json b/translations/ui-inventory/nn.json index 55d6107eb..7350fadaa 100644 --- a/translations/ui-inventory/nn.json +++ b/translations/ui-inventory/nn.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/pl.json b/translations/ui-inventory/pl.json index d79c0daf9..2c73cad99 100644 --- a/translations/ui-inventory/pl.json +++ b/translations/ui-inventory/pl.json @@ -652,7 +652,7 @@ "instance.successfullySaved": "The instance - HRID {hrid} has been successfully saved.", "holdingsPaneTitle": "Holdings • {location} • {callNumber}", "instanceRecordSubtitle": "{hrid} • Last updated: {updatedDate}", - "instance.saveError": "Saving instance failed", + "instance.saveError": "Nie udał się zapis instancji", "itemRecordWithDescriptionBW": "Item record ({materialType}, {status}, bound with)", "acq.polNumber": "POL number", "acq.orderStatus": "Order status", @@ -693,7 +693,7 @@ "instance.suppressedFromDiscovery": "Suppressed from discovery", "createMARCHoldings": "Add MARC holdings record", "exportInProgress": "Export in progress", - "acq.acqUnit": "Acquisition unit", + "acq.acqUnit": "Dział gromadzenia", "showMoreOptions": "Show more options", "more": "More", "noMatchingOptions": "No matching options", @@ -704,22 +704,22 @@ "administrativeNote": "Administrative note", "administrativeNotes": "Administrative notes", "browseCallNumbers": "Browse call numbers", - "browseSubjects": "Browse subjects", - "browse": "Browse", - "notLoadedMessage.browseCall": "Browse for results entering a query or choosing a filter.", - "title.browseCall": "Browse inventory", - "title.subTitle.browseCall": "Enter search criteria to start browsing", + "browseSubjects": "Przeglądaj hasła przedmiotowe", + "browse": "Przeglądaj", + "notLoadedMessage.browseCall": "Przeglądaj wyniki, wprowadzając zapytanie lub wybierając filtr", + "title.browseCall": "Przeglądaj inwentarz", + "title.subTitle.browseCall": "Wprowadź kryteria wyszukiwania, aby zacząć przeglądanie", "instances.columns.callNumber": "Call number", "instances.columns.numberOfTitles": "Number of titles", "viewAndReorderRequestsQueue": "View requests & reorder ({number})", - "browseCallNumbers.missedMatch": "would be here", - "search.allFields": "All", + "browseCallNumbers.missedMatch": "powinno być tutaj", + "search.allFields": "Wszystkie", "acq.dateOpened": "Date opened", "moveItems.selectHolding": "Select holdings", - "browseContributors": "Browse contributors", + "browseContributors": "Przeglądaj twórców", "newOrder": "New order", "permission.instance.createOrder": "Inventory: Create order from instance", - "search.oclc": "OCLC number, normalized", + "search.oclc": "Numer OCLC", "newOrder.modal.create": "Create", "newOrder.modal.label": "Create order", "newOrder.modal.message": "Selecting an existing order before clicking \"Create\", will add a new order line to the selected order for this title. Leaving the order number field blank and clicking \"Create\" will allow you to create a new purchase order and purchase order line for this title.", @@ -728,30 +728,30 @@ "instances.columns.contributor": "Contributor", "instances.columns.contributorType": "Type", "instances.columns.relatorTerm": "Relator term", - "search.holdings.uuid": "Holdings UUID", - "search.item.uuid": "Item UUID", + "search.holdings.uuid": "Identyfikator UUID zasobów", + "search.item.uuid": "Identyfikator UUID egzemplarza", "filters.nameType": "Name type", "instanceStatusShort": "Instance status", "effectiveCallNumberShelving": "Effective call number (item), shelving order", - "browseContributors.results.error": "Error returning results. Please retry or revise your search.", + "browseContributors.results.error": "Błąd podczas zwracania wyników. Ponów zapytanie lub zmień wyszukiwanie", "remote.warning.common.items": "Item has been successfully moved in FOLIO. To complete removing this item from remote storage, run an exception report or communicate this directly to your remote storage location.", - "search": "Search", + "search": "Szukaj", "linkedToMarcAuthority": "Linked to MARC authority", "browse.callNumbers": "Call numbers", - "browse.contributors": "Contributors", - "browse.searchableIndexesPlaceholder": "Select a browse option", - "browse.subjects": "Subjects", + "browse.contributors": "Twórcy", + "browse.searchableIndexesPlaceholder": "Wybierz opcje przeglądania", + "browse.subjects": "Hasło przedmiotowe", "noItemDeleteModal.awaitingPickupMessage": "Item HRID {hrid} with barcode {barcode} is checked out and can not be deleted until checked in again, and item status is 'Available'", "noItemDeleteModal.statusMessage": "Item HRID {hrid} with barcode {barcode} has the item status {status} and cannot be deleted until the item status is set to 'Available'.", - "search.instanceNotes": "Instance notes (all)", - "search.holdingsNotes": "Holdings notes (all)", - "search.itemNotes": "Item notes (all)", - "search.instanceAdministrativeNotes": "Instance administrative notes", - "search.holdingsAdministrativeNotes": "Holdings administrative notes", - "search.itemAdministrativeNotes": "Item administrative notes", - "inactive": "Inactive", + "search.instanceNotes": "Uwagi instancji (wszystkie)", + "search.holdingsNotes": "Uwagi zasobów (wszystkie)", + "search.itemNotes": "Uwagi egzemplarza (wszystkie)", + "search.instanceAdministrativeNotes": "Uwagi administracyjne instancji", + "search.holdingsAdministrativeNotes": "Uwagi administracyjne zasobów", + "search.itemAdministrativeNotes": "Uwagi administracyjne egzemplarza", + "inactive": "Nieaktywny", "inactive.paneTitle": "Inactive {location}", - "search.itemCirculationNotes": "Circulation notes", + "search.itemCirculationNotes": "Uwagi udostępniania", "inactive.gridCell": "Inactive {location}", "authorityId": "Authority UUID", "appMenu.inventorySearch": "Inventory app search", @@ -805,14 +805,14 @@ "item.status.unavailable.lowercase": "unavailable", "item.status.unknown.lowercase": "unknown", "item.status.withdrawn.lowercase": "withdrawn", - "browse.callNumbersAll": "Call numbers (all)", - "browse.dewey": "Dewey Decimal classification", - "browse.libOfCongress": "Library of Congress classification", + "browse.callNumbersAll": "Sygnatury (wszystkie)", + "browse.dewey": "Klasyfikacja Dziesiętna Deweya", + "browse.libOfCongress": "Klasyfikacja Biblioteki Kongresu", "browse.local": "Local", - "browse.natLibOfMed": "National Library of Medicine classification", + "browse.natLibOfMed": "Klasyfikacja National Library of Medicine", "browse.other": "Other scheme", "browse.superintendent": "Superintendent of Documents classification", - "advancedSearch": "Advanced search", + "advancedSearch": "Wyszukiwanie zaawansowane", "filters.shared": "Shared", "consortia.instanceRecordTitle": "{isShared, select, false {Local} true {Shared} other {}} instance • {title} {publisherAndDate}", "editInstance.consortia.title": "Edit {isShared, select, false {local} true {shared} other {}} instance • {title}", @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/pt_BR.json b/translations/ui-inventory/pt_BR.json index e21108f36..8c3b0eefd 100644 --- a/translations/ui-inventory/pt_BR.json +++ b/translations/ui-inventory/pt_BR.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Compartilhar", "shareLocalInstance.toast.unsuccessful": "A instância local {instanceTitle} não foi compartilhada", "shareLocalInstance.toast.successful": "A instância local {instanceTitle} foi compartilhada com sucesso", - "filters.tenantId": "Realizada por" + "filters.tenantId": "Realizada por", + "permission.consortia.inventory.share.local.instance": "Inventário: Compartilhar a instância local com o consórcio", + "consortialHoldings": "Participações em consórcios" } \ No newline at end of file diff --git a/translations/ui-inventory/pt_PT.json b/translations/ui-inventory/pt_PT.json index e62bd2284..c6cf829fd 100644 --- a/translations/ui-inventory/pt_PT.json +++ b/translations/ui-inventory/pt_PT.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/ru.json b/translations/ui-inventory/ru.json index 301f8e044..68ae312ae 100644 --- a/translations/ui-inventory/ru.json +++ b/translations/ui-inventory/ru.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/sv.json b/translations/ui-inventory/sv.json index 55d6107eb..7350fadaa 100644 --- a/translations/ui-inventory/sv.json +++ b/translations/ui-inventory/sv.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/ur.json b/translations/ui-inventory/ur.json index 4b5d7e496..6de9761fb 100644 --- a/translations/ui-inventory/ur.json +++ b/translations/ui-inventory/ur.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/zh_CN.json b/translations/ui-inventory/zh_CN.json index 69ce11649..9d71eaea0 100644 --- a/translations/ui-inventory/zh_CN.json +++ b/translations/ui-inventory/zh_CN.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "共享", "shareLocalInstance.toast.unsuccessful": "本地实例 {instanceTitle} 未共享", "shareLocalInstance.toast.successful": "本地实例 {instanceTitle} 已成功共享", - "filters.tenantId": "持有者" + "filters.tenantId": "持有者", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file diff --git a/translations/ui-inventory/zh_TW.json b/translations/ui-inventory/zh_TW.json index 55ecb0e8c..84fa6f650 100644 --- a/translations/ui-inventory/zh_TW.json +++ b/translations/ui-inventory/zh_TW.json @@ -827,5 +827,7 @@ "shareLocalInstance.modal.confirmButton": "Share", "shareLocalInstance.toast.unsuccessful": "Local instance {instanceTitle} was not shared", "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", - "filters.tenantId": "Held by" + "filters.tenantId": "Held by", + "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", + "consortialHoldings": "Consortial holdings" } \ No newline at end of file