From 80749c352148526769a58aa2263dbc0bb6c29e6a Mon Sep 17 00:00:00 2001 From: Mariia Aloshyna <55138456+mariia-aloshyna@users.noreply.github.com> Date: Thu, 19 Oct 2023 15:06:22 +0300 Subject: [PATCH 1/7] UIIN-2601: Instance. Series heading has vanished in detailed view (#2314) --- CHANGELOG.md | 1 + .../InstanceTitleData/InstanceTitleData.js | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 943853786..47d8adcf8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -12,6 +12,7 @@ * Edit instance success toast no longer shows the instance HRID. Fixes UIIN-2588. * Show facet options, if they exist, after clicking the +More button. Refs UIIN-2533. * If Shared & Held by facets were selected in the Browse search, then retain them in the Search lookup after clicking the record. Refs UIIN-2608. +* Instance. Series heading has vanished in detailed view. Fixes UIIN-2601. ## [10.0.0](https://github.com/folio-org/ui-inventory/tree/v10.0.0) (2023-10-13) [Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.12...v10.0.0) diff --git a/src/Instance/InstanceDetails/InstanceTitleData/InstanceTitleData.js b/src/Instance/InstanceDetails/InstanceTitleData/InstanceTitleData.js index c604f8945..f11e10e36 100644 --- a/src/Instance/InstanceDetails/InstanceTitleData/InstanceTitleData.js +++ b/src/Instance/InstanceDetails/InstanceTitleData/InstanceTitleData.js @@ -29,10 +29,13 @@ const InstanceTitleData = ({ }) => { const precedingTitles = useMemo(() => { return checkIfArrayIsEmpty(instance.precedingTitles); - }, [instance]); + }, [instance.precedingTitles]); const succeedingTitles = useMemo(() => { return checkIfArrayIsEmpty(instance.succeedingTitles); - }, [instance]); + }, [instance.succeedingTitles]); + const seriesStatements = useMemo(() => { + return checkIfArrayIsEmpty(instance.series); + }, [instance.series]); return ( From 3411d728c864542ce30a6a91a5affc1af6fec06a Mon Sep 17 00:00:00 2001 From: Oleksandr Hladchenko <85172747+OleksandrHladchenko1@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:38:24 +0300 Subject: [PATCH 2/7] UIIN-2600: Remove error message after switch from Instance Edit screen to another app (#2311) * UIIN-2600: Remove error message after switch from Instance Edit screen to another app * UIIN-2600: Use ternary * Update CHANGELOG.md --- CHANGELOG.md | 1 + .../ConsortialHoldings/ConsortialHoldings.js | 18 +++++++++--------- src/edit/InstanceForm.js | 4 ++-- 3 files changed, 12 insertions(+), 11 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 47d8adcf8..1b933a6f9 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -13,6 +13,7 @@ * Show facet options, if they exist, after clicking the +More button. Refs UIIN-2533. * If Shared & Held by facets were selected in the Browse search, then retain them in the Search lookup after clicking the record. Refs UIIN-2608. * Instance. Series heading has vanished in detailed view. Fixes UIIN-2601. +* Remove error message after switch from Instance Edit screen to another app. Fixes UIIN-2600. ## [10.0.0](https://github.com/folio-org/ui-inventory/tree/v10.0.0) (2023-10-13) [Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.12...v10.0.0) diff --git a/src/Instance/InstanceDetails/ConsortialHoldings/ConsortialHoldings.js b/src/Instance/InstanceDetails/ConsortialHoldings/ConsortialHoldings.js index aae864206..e2a220c2d 100644 --- a/src/Instance/InstanceDetails/ConsortialHoldings/ConsortialHoldings.js +++ b/src/Instance/InstanceDetails/ConsortialHoldings/ConsortialHoldings.js @@ -23,20 +23,13 @@ import { } from '../../../hooks'; const ConsortialHoldings = ({ instance }) => { + const pathToAccordion = ['consortialHoldings', '_state']; const instanceId = instance?.id; - const prevInstanceId = useRef(instanceId); const stripes = useStripes(); + const prevInstanceId = useRef(instanceId); const { consortiaTenantsById } = useContext(DataContext); - const { tenants } = useSearchForShadowInstanceTenants({ instanceId }); - - const memberTenants = tenants - .map(tenant => consortiaTenantsById[tenant.id]) - .filter(tenant => !tenant?.isCentral && (tenant?.id !== stripes.okapi.tenant)) - .sort((a, b) => a.name.localeCompare(b.name)); - - const pathToAccordion = ['consortialHoldings', '_state']; const [isConsortialAccOpen, setConsortialAccOpen] = useHoldingsAccordionState({ instanceId, pathToAccordion }); useEffect(() => { @@ -46,6 +39,13 @@ const ConsortialHoldings = ({ instance }) => { } }, [instanceId]); + if (!consortiaTenantsById) return null; + + const memberTenants = tenants + .map(tenant => consortiaTenantsById[tenant.id]) + .filter(tenant => !tenant?.isCentral && (tenant?.id !== stripes.okapi.tenant)) + .sort((a, b) => a.name.localeCompare(b.name)); + return ( ({ label: refLookup(referenceTables.statisticalCodeTypes, code.statisticalCodeTypeId).name + ': ' + code.code + ' - ' + code.name, @@ -349,7 +349,7 @@ class InstanceForm extends React.Component { selected: code.id === initialValues.statisticalCodeId, }) ) - .sort((a, b) => a.label.localeCompare(b.label)); + .sort((a, b) => a.label.localeCompare(b.label)) : []; // Since preceding/succeeding title relationships are split out from other parent/child instances, // we don't want the type selection box for parent/child to include the preceding-succeeding type From 7c2fc43e5350c9869df23d059bfc1a86892b0109 Mon Sep 17 00:00:00 2001 From: FOLIO Translations Bot <38661258+folio-translations@users.noreply.github.com> Date: Thu, 19 Oct 2023 16:51:54 -0400 Subject: [PATCH 3/7] Lokalise: updates --- translations/ui-inventory/ar.json | 5 ++++- translations/ui-inventory/ber.json | 5 ++++- translations/ui-inventory/ca.json | 5 ++++- translations/ui-inventory/cs_CZ.json | 5 ++++- translations/ui-inventory/da.json | 5 ++++- translations/ui-inventory/de.json | 5 ++++- translations/ui-inventory/en_GB.json | 5 ++++- translations/ui-inventory/en_SE.json | 5 ++++- translations/ui-inventory/en_US.json | 5 ++++- translations/ui-inventory/es.json | 5 ++++- translations/ui-inventory/es_419.json | 5 ++++- translations/ui-inventory/es_ES.json | 5 ++++- translations/ui-inventory/fr.json | 5 ++++- translations/ui-inventory/fr_FR.json | 5 ++++- translations/ui-inventory/he.json | 5 ++++- translations/ui-inventory/hi_IN.json | 5 ++++- translations/ui-inventory/hu.json | 5 ++++- translations/ui-inventory/it_IT.json | 5 ++++- translations/ui-inventory/ja.json | 5 ++++- translations/ui-inventory/ko.json | 5 ++++- translations/ui-inventory/nb.json | 5 ++++- translations/ui-inventory/nn.json | 5 ++++- translations/ui-inventory/pl.json | 5 ++++- translations/ui-inventory/pt_BR.json | 5 ++++- translations/ui-inventory/pt_PT.json | 5 ++++- translations/ui-inventory/ru.json | 5 ++++- translations/ui-inventory/sv.json | 5 ++++- translations/ui-inventory/ur.json | 5 ++++- translations/ui-inventory/zh_CN.json | 5 ++++- translations/ui-inventory/zh_TW.json | 5 ++++- 30 files changed, 120 insertions(+), 30 deletions(-) diff --git a/translations/ui-inventory/ar.json b/translations/ui-inventory/ar.json index 161a0e0b9..dbd851a8e 100644 --- a/translations/ui-inventory/ar.json +++ b/translations/ui-inventory/ar.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/ber.json b/translations/ui-inventory/ber.json index 7350fadaa..bae5522b2 100644 --- a/translations/ui-inventory/ber.json +++ b/translations/ui-inventory/ber.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/ca.json b/translations/ui-inventory/ca.json index 8383b2062..0aaa3028b 100644 --- a/translations/ui-inventory/ca.json +++ b/translations/ui-inventory/ca.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/cs_CZ.json b/translations/ui-inventory/cs_CZ.json index 7c5f19a7e..50e27de2d 100644 --- a/translations/ui-inventory/cs_CZ.json +++ b/translations/ui-inventory/cs_CZ.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/da.json b/translations/ui-inventory/da.json index 045ab062d..2073be45c 100644 --- a/translations/ui-inventory/da.json +++ b/translations/ui-inventory/da.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/de.json b/translations/ui-inventory/de.json index 6c1959203..5c6b5fd57 100644 --- a/translations/ui-inventory/de.json +++ b/translations/ui-inventory/de.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/en_GB.json b/translations/ui-inventory/en_GB.json index 7350fadaa..bae5522b2 100644 --- a/translations/ui-inventory/en_GB.json +++ b/translations/ui-inventory/en_GB.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/en_SE.json b/translations/ui-inventory/en_SE.json index 7350fadaa..bae5522b2 100644 --- a/translations/ui-inventory/en_SE.json +++ b/translations/ui-inventory/en_SE.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/en_US.json b/translations/ui-inventory/en_US.json index 7350fadaa..bae5522b2 100644 --- a/translations/ui-inventory/en_US.json +++ b/translations/ui-inventory/en_US.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/es.json b/translations/ui-inventory/es.json index abff5b97e..4fed8ecc0 100644 --- a/translations/ui-inventory/es.json +++ b/translations/ui-inventory/es.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/es_419.json b/translations/ui-inventory/es_419.json index a3fc24001..d351bcd30 100644 --- a/translations/ui-inventory/es_419.json +++ b/translations/ui-inventory/es_419.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/es_ES.json b/translations/ui-inventory/es_ES.json index 42099fcb0..d7a2b120f 100644 --- a/translations/ui-inventory/es_ES.json +++ b/translations/ui-inventory/es_ES.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/fr.json b/translations/ui-inventory/fr.json index 292eec37e..fda3aba85 100644 --- a/translations/ui-inventory/fr.json +++ b/translations/ui-inventory/fr.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/fr_FR.json b/translations/ui-inventory/fr_FR.json index 02ab60902..f5453b9b0 100644 --- a/translations/ui-inventory/fr_FR.json +++ b/translations/ui-inventory/fr_FR.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/he.json b/translations/ui-inventory/he.json index 32e4e2273..776d84c66 100644 --- a/translations/ui-inventory/he.json +++ b/translations/ui-inventory/he.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/hi_IN.json b/translations/ui-inventory/hi_IN.json index 7350fadaa..bae5522b2 100644 --- a/translations/ui-inventory/hi_IN.json +++ b/translations/ui-inventory/hi_IN.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/hu.json b/translations/ui-inventory/hu.json index 66785515d..8f25c7dc1 100644 --- a/translations/ui-inventory/hu.json +++ b/translations/ui-inventory/hu.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/it_IT.json b/translations/ui-inventory/it_IT.json index ebcb79611..95d44b31b 100644 --- a/translations/ui-inventory/it_IT.json +++ b/translations/ui-inventory/it_IT.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/ja.json b/translations/ui-inventory/ja.json index 0761df8f7..a6687b24b 100644 --- a/translations/ui-inventory/ja.json +++ b/translations/ui-inventory/ja.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/ko.json b/translations/ui-inventory/ko.json index d396b651f..a6f02ca10 100644 --- a/translations/ui-inventory/ko.json +++ b/translations/ui-inventory/ko.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/nb.json b/translations/ui-inventory/nb.json index 7350fadaa..bae5522b2 100644 --- a/translations/ui-inventory/nb.json +++ b/translations/ui-inventory/nb.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/nn.json b/translations/ui-inventory/nn.json index 7350fadaa..bae5522b2 100644 --- a/translations/ui-inventory/nn.json +++ b/translations/ui-inventory/nn.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/pl.json b/translations/ui-inventory/pl.json index 2c73cad99..c27026f94 100644 --- a/translations/ui-inventory/pl.json +++ b/translations/ui-inventory/pl.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/pt_BR.json b/translations/ui-inventory/pt_BR.json index 8c3b0eefd..b493cbb64 100644 --- a/translations/ui-inventory/pt_BR.json +++ b/translations/ui-inventory/pt_BR.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "A instância local {instanceTitle} foi compartilhada com sucesso", "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" + "consortialHoldings": "Participações em consórcios", + "unlinkLocalMarcAuthorities.modal.header": "Vinculado às autoridades locais", + "unlinkLocalMarcAuthorities.modal.message": "Se prosseguir com o compartilhamento dessa instância, os campos bibliográficos {linkedAuthoritiesLength} vinculados a registros de autoridades locais manterão os valores autorizados, mas passarão a não ser controlados", + "unlinkLocalMarcAuthorities.modal.proceed": "Prosseguir" } \ No newline at end of file diff --git a/translations/ui-inventory/pt_PT.json b/translations/ui-inventory/pt_PT.json index c6cf829fd..dea889670 100644 --- a/translations/ui-inventory/pt_PT.json +++ b/translations/ui-inventory/pt_PT.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/ru.json b/translations/ui-inventory/ru.json index 68ae312ae..ea41906a8 100644 --- a/translations/ui-inventory/ru.json +++ b/translations/ui-inventory/ru.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/sv.json b/translations/ui-inventory/sv.json index 7350fadaa..bae5522b2 100644 --- a/translations/ui-inventory/sv.json +++ b/translations/ui-inventory/sv.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/ur.json b/translations/ui-inventory/ur.json index 6de9761fb..fbf98c126 100644 --- a/translations/ui-inventory/ur.json +++ b/translations/ui-inventory/ur.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/zh_CN.json b/translations/ui-inventory/zh_CN.json index 9d71eaea0..e6a0b9a3c 100644 --- a/translations/ui-inventory/zh_CN.json +++ b/translations/ui-inventory/zh_CN.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "本地实例 {instanceTitle} 已成功共享", "filters.tenantId": "持有者", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file diff --git a/translations/ui-inventory/zh_TW.json b/translations/ui-inventory/zh_TW.json index 84fa6f650..6d009f998 100644 --- a/translations/ui-inventory/zh_TW.json +++ b/translations/ui-inventory/zh_TW.json @@ -829,5 +829,8 @@ "shareLocalInstance.toast.successful": "Local instance {instanceTitle} has been successfully shared", "filters.tenantId": "Held by", "permission.consortia.inventory.share.local.instance": "Inventory: Share local instance with consortium", - "consortialHoldings": "Consortial holdings" + "consortialHoldings": "Consortial holdings", + "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" } \ No newline at end of file From 90f2bfff0395ac3ab9340aeecb31ab9b126b18c2 Mon Sep 17 00:00:00 2001 From: Oleksandr Hladchenko <85172747+OleksandrHladchenko1@users.noreply.github.com> Date: Fri, 20 Oct 2023 15:10:55 +0300 Subject: [PATCH 4/7] UIIN-2452: Enable/disable consortial holdings/item actions based on User permissions (#2284) * Consortial holdings acc * UIIN-2410: Adjustments * UIIN-2410: Add new hook * UIIN-2410: Add tests * UIIN-2452: Disable buttons when member tenant does not have permissions * UIIN-2410: Instance 3rd pane: Add consortial holdings/item accordion * UIIN-2452: Add unit tests & switching affiliation when view holdings and add item * UIIN-2410: Fix tests * UIIN-2452: Add switching affiliation when click on item barcode & Add holdings button * UIIN-2452: Fix tests * Update HoldingAccordion.js * Update HoldingButtonsGroup.js * UIIN-2452: Fix tests * Update HoldingsListMovement.js * Update HoldingContainer.js * UIIN-2452: Add returning to the previous affiliation * UIIN-2452: Add tenantId to props validation * UIIN-2452: Add behaviour for non-consortial tenant * Fix some comments * Fix perms handling * Fix warnings * Adjust tests * Fix tests * UIIN-2452: Switch user affiliation using validateUser * Update HoldingAccordion.js * Supress Add holding & Add item & View holdings buttons if user doesn't have permissions * UIIN-2452: Fix comments & add unit tests * UIIN-2452: Fixes after review * UIIN-2452: Fix code smells --------- Co-authored-by: Mariia_Aloshyna Co-authored-by: Mariia Aloshyna <55138456+mariia-aloshyna@users.noreply.github.com> --- CHANGELOG.md | 1 + src/Holding/CreateHolding/CreateHolding.js | 13 ++-- .../HoldingBoundWith/HoldingBoundWith.js | 8 ++- .../HoldingBoundWith/useBoundWithHoldings.js | 5 +- .../useBoundWithHoldings.test.js | 14 ++-- src/Instance/HoldingsList/Holding/Holding.js | 14 +++- .../HoldingsList/Holding/HoldingAccordion.js | 10 ++- .../Holding/HoldingAccordion.test.js | 6 +- .../Holding/HoldingButtonsGroup.js | 30 +++++--- .../Holding/HoldingButtonsGroup.test.js | 49 ++++++++++--- .../HoldingsList/Holding/HoldingContainer.js | 37 +++++++++- .../Holding/HoldingContainer.test.js | 2 + src/Instance/HoldingsList/HoldingsList.js | 11 ++- .../HoldingsList/HoldingsListContainer.js | 12 ++++ .../HoldingsListContainer.test.js | 5 +- .../ConsortialHoldings/ConsortialHoldings.js | 11 ++- .../InstanceDetails/InstanceDetails.js | 18 ++++- .../InstanceNewHolding/InstanceNewHolding.js | 28 ++++++-- .../InstanceNewHolding.test.js | 24 ++++++- .../MemberTenantHoldings.js | 18 ++++- .../MemberTenantHoldings.test.js | 9 +++ .../HoldingsListMovement.js | 10 ++- src/Instance/ItemsList/ItemBarcode.js | 64 ++++++++++++----- src/Instance/ItemsList/ItemsList.js | 20 +++++- src/Instance/ItemsList/ItemsListContainer.js | 6 +- .../ItemsList/tests/ItemBarcode.test.js | 32 ++++++++- src/Item/CreateItem/CreateItem.js | 38 +++++++---- src/Item/CreateItem/CreateItem.test.js | 17 ++++- src/View.css | 20 +++++- src/ViewHoldingsRecord.js | 7 +- src/ViewHoldingsRecord.test.js | 14 +++- src/ViewInstance.js | 32 ++++++++- src/ViewInstance.test.js | 64 +++++++++++++---- src/common/hooks/useHolding/useHolding.js | 8 ++- src/constants.js | 2 + src/hooks/useChunkedCQLFetch.js | 7 +- src/routes/ItemRoute.js | 31 ++++++--- src/utils.js | 68 +++++++++++++++++++ src/utils.test.js | 35 ++++++++++ src/views/ItemView.js | 10 +-- test/jest/__mock__/stripesCore.mock.js | 4 ++ 41 files changed, 671 insertions(+), 143 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 1b933a6f9..b58071dda 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -14,6 +14,7 @@ * If Shared & Held by facets were selected in the Browse search, then retain them in the Search lookup after clicking the record. Refs UIIN-2608. * Instance. Series heading has vanished in detailed view. Fixes UIIN-2601. * Remove error message after switch from Instance Edit screen to another app. Fixes UIIN-2600. +* Enable/disable consortial holdings/item actions based on User permissions. Refs UIIN-2452. ## [10.0.0](https://github.com/folio-org/ui-inventory/tree/v10.0.0) (2023-10-13) [Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.12...v10.0.0) diff --git a/src/Holding/CreateHolding/CreateHolding.js b/src/Holding/CreateHolding/CreateHolding.js index b18b335fe..1cfcceef9 100644 --- a/src/Holding/CreateHolding/CreateHolding.js +++ b/src/Holding/CreateHolding/CreateHolding.js @@ -10,15 +10,14 @@ import { stripesConnect, stripesShape, } from '@folio/stripes/core'; -import { - LoadingView, -} from '@folio/stripes/components'; +import { LoadingView } from '@folio/stripes/components'; import { useInstance, } from '../../common/hooks'; import useCallout from '../../hooks/useCallout'; import HoldingsForm from '../../edit/holdings/HoldingsForm'; +import { switchAffiliation } from '../../utils'; const CreateHolding = ({ history, @@ -31,14 +30,19 @@ const CreateHolding = ({ const callout = useCallout(); const { instance, isLoading: isInstanceLoading } = useInstance(instanceId); const sourceId = referenceData.holdingsSourcesByName?.FOLIO?.id; + const { location: { state: { tenantFrom } } } = history; - const onCancel = useCallback(() => { + const goBack = useCallback(() => { history.push({ pathname: `/inventory/view/${instanceId}`, search: location.search, }); }, [location.search, instanceId]); + const onCancel = useCallback(() => { + switchAffiliation(stripes, tenantFrom, goBack); + }, [stripes, tenantFrom, goBack]); + const onSubmit = useCallback((newHolding) => { return mutator.holding.POST(newHolding) .then((holdingsRecord) => { @@ -93,7 +97,6 @@ CreateHolding.manifest = Object.freeze({ CreateHolding.propTypes = { location: PropTypes.object.isRequired, history: PropTypes.object.isRequired, - instanceId: PropTypes.string.isRequired, mutator: PropTypes.object.isRequired, referenceData: PropTypes.object.isRequired, diff --git a/src/Holding/ViewHolding/HoldingBoundWith/HoldingBoundWith.js b/src/Holding/ViewHolding/HoldingBoundWith/HoldingBoundWith.js index adadac33e..351feaae3 100644 --- a/src/Holding/ViewHolding/HoldingBoundWith/HoldingBoundWith.js +++ b/src/Holding/ViewHolding/HoldingBoundWith/HoldingBoundWith.js @@ -8,15 +8,19 @@ import { MultiColumnList, } from '@folio/stripes/components'; -import { IntlConsumer } from '@folio/stripes/core'; +import { + IntlConsumer, + useStripes, +} from '@folio/stripes/core'; import { noValue } from '../../../constants'; import { checkIfArrayIsEmpty } from '../../../utils'; import useBoundWithItems from './useBoundWithItems'; import useBoundWithHoldings from './useBoundWithHoldings'; const HoldingBoundWith = ({ boundWithParts }) => { + const { okapi: { tenant: tenantId } } = useStripes(); const { boundWithItems } = useBoundWithItems(boundWithParts); - const { isLoading, boundWithHoldings } = useBoundWithHoldings(boundWithItems); + const { isLoading, boundWithHoldings } = useBoundWithHoldings(boundWithItems, tenantId); const boundWithHoldingsMapById = keyBy(boundWithHoldings, 'id'); const data = boundWithItems?.map(boundWithItem => ({ item: boundWithItem, diff --git a/src/Holding/ViewHolding/HoldingBoundWith/useBoundWithHoldings.js b/src/Holding/ViewHolding/HoldingBoundWith/useBoundWithHoldings.js index 448627f80..790701396 100644 --- a/src/Holding/ViewHolding/HoldingBoundWith/useBoundWithHoldings.js +++ b/src/Holding/ViewHolding/HoldingBoundWith/useBoundWithHoldings.js @@ -1,6 +1,6 @@ import useChunkedCQLFetch from '../../../hooks/useChunkedCQLFetch'; -const useBoundWithHoldings = (boundWithItems) => { +const useBoundWithHoldings = (boundWithItems, tenantId) => { let holdingsRecordIds = boundWithItems?.map(x => x.holdingsRecordId); // De-dup the list of holdingsRecordIds for efficiency @@ -13,7 +13,8 @@ const useBoundWithHoldings = (boundWithItems) => { holdingQueries.reduce((acc, curr) => { return [...acc, ...(curr?.data?.holdingsRecords ?? [])]; }, []) - ) + ), + tenantId, }); return { diff --git a/src/Holding/ViewHolding/HoldingBoundWith/useBoundWithHoldings.test.js b/src/Holding/ViewHolding/HoldingBoundWith/useBoundWithHoldings.test.js index 97fa1be5c..eadd9393e 100644 --- a/src/Holding/ViewHolding/HoldingBoundWith/useBoundWithHoldings.test.js +++ b/src/Holding/ViewHolding/HoldingBoundWith/useBoundWithHoldings.test.js @@ -7,10 +7,14 @@ import { renderHook, act } from '@folio/jest-config-stripes/testing-library/reac import '../../../../test/jest/__mock__'; -import { useOkapiKy } from '@folio/stripes/core'; - import { boundWithHoldingsRecords } from './fixtures'; import useBoundWithHoldings from './useBoundWithHoldings'; +import { useTenantKy } from '../../../common'; + +jest.mock('../../../common', () => ({ + ...jest.requireActual('../../../common'), + useTenantKy: jest.fn(), +})); const queryClient = new QueryClient(); const wrapper = ({ children }) => ( @@ -21,7 +25,7 @@ const wrapper = ({ children }) => ( describe('useBoundWithHoldings', () => { beforeEach(() => { - useOkapiKy.mockClear().mockReturnValue({ + useTenantKy.mockClear().mockReturnValue({ get: () => ({ json: () => Promise.resolve({ holdingsRecords: boundWithHoldingsRecords }), }), @@ -31,7 +35,7 @@ describe('useBoundWithHoldings', () => { it('should fetch bound-with holdings', async () => { const boundWithItems = [{ hrid: 'BW-ITEM-1', holdingsRecordId: '9e8dc8ce-68f3-4e75-8479-d548ce521157' }]; - const { result } = renderHook(() => useBoundWithHoldings(boundWithItems), { wrapper }); + const { result } = renderHook(() => useBoundWithHoldings(boundWithItems, 'testTenantId'), { wrapper }); await act(() => !result.current.isLoading); @@ -39,7 +43,7 @@ describe('useBoundWithHoldings', () => { }); it('should not fetch bound-with holdings', async () => { - const { result } = renderHook(() => useBoundWithHoldings([]), { wrapper }); + const { result } = renderHook(() => useBoundWithHoldings([], 'testTenantId'), { wrapper }); expect(result.current.isLoading).toBe(false); expect(result.current.boundWithHoldings).toEqual([]); diff --git a/src/Instance/HoldingsList/Holding/Holding.js b/src/Instance/HoldingsList/Holding/Holding.js index aea2e7d98..8a0b1adb9 100644 --- a/src/Instance/HoldingsList/Holding/Holding.js +++ b/src/Instance/HoldingsList/Holding/Holding.js @@ -4,7 +4,10 @@ import { FormattedMessage } from 'react-intl'; import { Checkbox } from '@folio/stripes/components'; -import { ItemsListContainer, DropZone } from '../../ItemsList'; +import { + ItemsListContainer, + DropZone, +} from '../../ItemsList'; import HoldingAccordion from './HoldingAccordion'; @@ -20,6 +23,9 @@ const Holding = ({ isDraggable, isItemsDroppable, tenantId, + showViewHoldingsButton, + showAddItemButton, + isBarcodeAsHotlink, instanceId, pathToAccordionsState, }) => { @@ -53,12 +59,15 @@ const Holding = ({ tenantId={tenantId} instanceId={instanceId} pathToAccordionsState={pathToAccordionsState} + showViewHoldingsButton={showViewHoldingsButton} + showAddItemButton={showAddItemButton} > @@ -79,6 +88,9 @@ Holding.propTypes = { isHoldingDragSelected: PropTypes.func, isItemsDroppable: PropTypes.bool, tenantId: PropTypes.string, + showViewHoldingsButton: PropTypes.bool, + showAddItemButton: PropTypes.bool, + isBarcodeAsHotlink: PropTypes.bool, pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), }; diff --git a/src/Instance/HoldingsList/Holding/HoldingAccordion.js b/src/Instance/HoldingsList/Holding/HoldingAccordion.js index 02b4c2ff4..401cbc991 100644 --- a/src/Instance/HoldingsList/Holding/HoldingAccordion.js +++ b/src/Instance/HoldingsList/Holding/HoldingAccordion.js @@ -11,11 +11,12 @@ import { } from '@folio/stripes/components'; import { callNumberLabel } from '../../../utils'; + import HoldingButtonsGroup from './HoldingButtonsGroup'; -import useHoldingItemsQuery from '../../../hooks/useHoldingItemsQuery'; import { useHoldingsAccordionState, useLocationsQuery, + useHoldingItemsQuery, } from '../../../hooks'; const HoldingAccordion = ({ @@ -26,6 +27,8 @@ const HoldingAccordion = ({ onAddItem, withMoveDropdown, tenantId, + showViewHoldingsButton, + showAddItemButton, instanceId, pathToAccordionsState, }) => { @@ -63,6 +66,9 @@ const HoldingAccordion = ({ onAddItem={onAddItem} withMoveDropdown={withMoveDropdown} isOpen={open} + tenantId={tenantId} + showViewHoldingsButton={showViewHoldingsButton} + showAddItemButton={showAddItemButton} />; const location = labelLocation?.isActive ? @@ -125,6 +131,8 @@ HoldingAccordion.propTypes = { children: PropTypes.object, tenantId: PropTypes.string, pathToAccordionsState: PropTypes.arrayOf(PropTypes.string), + showViewHoldingsButton: PropTypes.bool, + showAddItemButton: PropTypes.bool, }; HoldingAccordion.defaultProps = { pathToAccordionsState: [] }; diff --git a/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js b/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js index b8aa058f9..a8e6bead9 100644 --- a/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js +++ b/src/Instance/HoldingsList/Holding/HoldingAccordion.test.js @@ -9,8 +9,9 @@ import '../../../../test/jest/__mock__'; import renderWithIntl from '../../../../test/jest/helpers/renderWithIntl'; import translations from '../../../../test/jest/helpers/translationsProperties'; import { items as itemsFixture } from '../../../../test/fixtures/items'; +import { useHoldingItemsQuery } from '../../../hooks'; + import HoldingAccordion from './HoldingAccordion'; -import useHoldingItemsQuery from '../../../hooks/useHoldingItemsQuery'; jest.mock('../../../hooks/useHoldingItemsQuery', () => jest.fn()); jest.mock('../../../hooks', () => ({ @@ -23,7 +24,8 @@ jest.mock('../../../hooks', () => ({ isActive: false, }, ], - }) + }), + useHoldingItemsQuery: jest.fn(), })); const HoldingAccordionSetup = () => ( diff --git a/src/Instance/HoldingsList/Holding/HoldingButtonsGroup.js b/src/Instance/HoldingsList/Holding/HoldingButtonsGroup.js index d6f3639e6..56cd5a859 100644 --- a/src/Instance/HoldingsList/Holding/HoldingButtonsGroup.js +++ b/src/Instance/HoldingsList/Holding/HoldingButtonsGroup.js @@ -13,6 +13,8 @@ import { Icon, } from '@folio/stripes/components'; +import { switchAffiliation } from '../../../utils'; + import { MoveToDropdown } from './MoveToDropdown'; const HoldingButtonsGroup = ({ @@ -24,6 +26,9 @@ const HoldingButtonsGroup = ({ onAddItem, itemCount, isOpen, + tenantId, + showViewHoldingsButton, + showAddItemButton, }) => { const stripes = useStripes(); const isUserInCentralTenant = checkIfUserInCentralTenant(stripes); @@ -39,20 +44,21 @@ const HoldingButtonsGroup = ({ /> ) } - - - {!isUserInCentralTenant && ( + {showViewHoldingsButton && + + } + {!isUserInCentralTenant && showAddItemButton && ( @@ -45,6 +60,7 @@ const InstanceNewHolding = ({ InstanceNewHolding.propTypes = { location: PropTypes.object.isRequired, instance: PropTypes.object, + tenantId: PropTypes.string, }; export default withRouter(InstanceNewHolding); diff --git a/src/Instance/InstanceDetails/InstanceNewHolding/InstanceNewHolding.test.js b/src/Instance/InstanceDetails/InstanceNewHolding/InstanceNewHolding.test.js index 9e51f5254..8cffc1fae 100644 --- a/src/Instance/InstanceDetails/InstanceNewHolding/InstanceNewHolding.test.js +++ b/src/Instance/InstanceDetails/InstanceNewHolding/InstanceNewHolding.test.js @@ -1,11 +1,24 @@ import React from 'react'; import { BrowserRouter as Router } from 'react-router-dom'; +import { createMemoryHistory } from 'history'; + +import { fireEvent } from '@folio/jest-config-stripes/testing-library/react'; import '../../../../test/jest/__mock__'; import renderWithIntl from '../../../../test/jest/helpers/renderWithIntl'; import InstanceNewHolding from './InstanceNewHolding'; +const mockPush = jest.fn(); + +const history = createMemoryHistory(); +history.push = mockPush; + +jest.mock('../../../utils', () => ({ + ...jest.requireActual('../../../utils'), + switchAffiliation: jest.fn(() => mockPush()), +})); + const props = { location: {}, instance: {}, @@ -13,7 +26,7 @@ const props = { const renderInstanceNewHolding = () => ( renderWithIntl( - + ) @@ -24,4 +37,13 @@ describe('InstanceNewHolding', () => { const { getByText } = renderInstanceNewHolding(); expect(getByText(/ui-inventory.addHoldings/i)).toBeInTheDocument(); }); + + describe('when click "Add holdings" button', () => { + it('should redirect to the Holdings form', () => { + const { getByText } = renderInstanceNewHolding(); + fireEvent.click(getByText(/ui-inventory.addHoldings/i)); + + expect(mockPush).toHaveBeenCalledWith(); + }); + }); }); diff --git a/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js b/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js index 47fc6aa2f..347164e0f 100644 --- a/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js +++ b/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.js @@ -16,6 +16,7 @@ import { InstanceNewHolding } from '../InstanceNewHolding'; import { MoveItemsContext } from '../../MoveItemsContext'; import { useInstanceHoldingsQuery } from '../../../providers'; +import { hasMemberTenantPermission } from '../../../utils'; import { useHoldingsAccordionState } from '../../../hooks'; import css from './MemberTenantHoldings.css'; @@ -23,6 +24,7 @@ import css from './MemberTenantHoldings.css'; const MemberTenantHoldings = ({ memberTenant, instance, + userTenantPermissions, }) => { const { name, @@ -38,6 +40,11 @@ const MemberTenantHoldings = ({ const { holdingsRecords, isLoading } = useInstanceHoldingsQuery(instance?.id, { tenantId: id }); const isUserInCentralTenant = checkIfUserInCentralTenant(stripes); + const canViewHoldings = hasMemberTenantPermission('ui-inventory.instance.view', id, userTenantPermissions); + const canCreateItem = hasMemberTenantPermission('ui-inventory.item.edit', id, userTenantPermissions); + const canCreateHoldings = hasMemberTenantPermission('ui-inventory.holdings.edit', id, userTenantPermissions); + const canViewItems = hasMemberTenantPermission('ui-inventory.instance.view', id, userTenantPermissions); + if (isEmpty(holdingsRecords)) return null; return ( @@ -59,13 +66,19 @@ const MemberTenantHoldings = ({ tenantId={id} draggable={false} droppable={false} + showViewHoldingsButton={canViewHoldings} + showAddItemButton={canCreateItem} + isBarcodeAsHotlink={canViewItems} pathToAccordionsState={pathToHoldingsAccordion} /> )} - {!isUserInCentralTenant && ( - + {!isUserInCentralTenant && canCreateHoldings && ( + )} ); @@ -74,6 +87,7 @@ const MemberTenantHoldings = ({ MemberTenantHoldings.propTypes = { instance: PropTypes.object.isRequired, memberTenant: PropTypes.object.isRequired, + userTenantPermissions: PropTypes.arrayOf(PropTypes.object), }; export default MemberTenantHoldings; diff --git a/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.test.js b/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.test.js index 09c060e8b..f6271d607 100644 --- a/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.test.js +++ b/src/Instance/InstanceDetails/MemberTenantHoldings/MemberTenantHoldings.test.js @@ -30,12 +30,21 @@ const mockMemberTenant = { name: 'College', }; +const userTenantPermissions = [{ + tenantId: 'college', + permissionNames: [{ + permissionName: 'ui-inventory.holdings.edit', + subPermissions: ['test subPermission 1'] + }], +}]; + const renderMemberTenantHoldings = () => { const component = ( ); diff --git a/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js b/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js index 825a8b514..009c66090 100644 --- a/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js +++ b/src/Instance/InstanceMovement/HoldingMovementList/HoldingsListMovement.js @@ -23,6 +23,9 @@ const HoldingsListMovement = ({ draggable, droppable, tenantId, + showViewHoldingsButton, + showAddItemButton, + isBarcodeAsHotlink, pathToAccordionsState, }) => { const { @@ -59,6 +62,9 @@ const HoldingsListMovement = ({ holdingIndex={index} draggingHoldingsCount={draggingHoldingsCount} tenantId={tenantId} + showViewHoldingsButton={showViewHoldingsButton} + showAddItemButton={showAddItemButton} + isBarcodeAsHotlink={isBarcodeAsHotlink} pathToAccordionsState={pathToAccordionsState} /> )) @@ -78,7 +84,9 @@ const HoldingsListMovement = ({ HoldingsListMovement.propTypes = { instance: PropTypes.object.isRequired, holdings: PropTypes.arrayOf(PropTypes.object), - + showViewHoldingsButton: PropTypes.bool, + showAddItemButton: PropTypes.bool, + isBarcodeAsHotlink: PropTypes.bool, draggable: PropTypes.bool, droppable: PropTypes.bool, tenantId: PropTypes.string, diff --git a/src/Instance/ItemsList/ItemBarcode.js b/src/Instance/ItemsList/ItemBarcode.js index 621febe1c..a009ff090 100644 --- a/src/Instance/ItemsList/ItemBarcode.js +++ b/src/Instance/ItemsList/ItemBarcode.js @@ -4,31 +4,50 @@ import React, { } from 'react'; import PropTypes from 'prop-types'; import { withRouter } from 'react-router'; -import { - Link, -} from 'react-router-dom'; -import { - FormattedMessage, -} from 'react-intl'; +import { useHistory } from 'react-router-dom'; +import { FormattedMessage } from 'react-intl'; import { CopyToClipboard } from 'react-copy-to-clipboard'; import queryString from 'query-string'; import { CalloutContext, AppIcon, + useStripes, } from '@folio/stripes/core'; import { + Button, Highlighter, IconButton, } from '@folio/stripes/components'; import css from '../../View.css'; import { QUERY_INDEXES } from '../../constants'; +import { switchAffiliation } from '../../utils'; -const ItemBarcode = ({ location, item, holdingId, instanceId }) => { +const ItemBarcode = ({ + location, + item, + holdingId, + instanceId, + isBarcodeAsHotlink, + tenantId, +}) => { + const history = useHistory(); + const stripes = useStripes(); const { search } = location; const queryBarcode = queryString.parse(search)?.query; const isQueryByBarcode = queryString.parse(search)?.qindex === QUERY_INDEXES.BARCODE; + const onViewItem = useCallback(() => { + history.push({ + pathname: `/inventory/view/${instanceId}/${holdingId}/${item.id}`, + search, + state: { + tenantTo: tenantId, + tenantFrom: stripes.okapi.tenant, + }, + }); + }, [instanceId, holdingId, item.id, search]); + const callout = useContext(CalloutContext); const onCopyToClipbaord = useCallback(() => { callout.sendCallout({ @@ -43,18 +62,26 @@ const ItemBarcode = ({ location, item, holdingId, instanceId }) => { const highlightableBarcode = isQueryByBarcode ? : item.barcode; + const itemBarcode = ( + + + {item.barcode ? highlightableBarcode : } + + + ); + return ( <> - - - - {item.barcode ? highlightableBarcode : } - - - + {isBarcodeAsHotlink ? ( + + ) : itemBarcode + } {item.barcode && { ItemBarcode.propTypes = { location: PropTypes.object.isRequired, - item: PropTypes.object.isRequired, holdingId: PropTypes.string.isRequired, instanceId: PropTypes.string.isRequired, + tenantId: PropTypes.string, + isBarcodeAsHotlink: PropTypes.bool, }; export default withRouter(ItemBarcode); diff --git a/src/Instance/ItemsList/ItemsList.js b/src/Instance/ItemsList/ItemsList.js index 0cbb216fa..733251305 100644 --- a/src/Instance/ItemsList/ItemsList.js +++ b/src/Instance/ItemsList/ItemsList.js @@ -40,6 +40,8 @@ const getFormatter = ( holdingsMapById, selectItemsForDrag, ifItemsSelected, + isBarcodeAsHotlink, + tenantId, ) => ({ 'dnd': () => ( {item.discoverySuppress && @@ -160,8 +164,10 @@ const ItemsList = ({ selectItemsForDrag, getDraggingItems, isFetching, + isBarcodeAsHotlink, + tenantId, }) => { - const { boundWithHoldings: holdings, isLoading } = useBoundWithHoldings(items); + const { boundWithHoldings: holdings, isLoading } = useBoundWithHoldings(items, tenantId); const holdingsMapById = keyBy(holdings, 'id'); const intl = useIntl(); const [itemsSorting, setItemsSorting] = useState({ @@ -180,7 +186,15 @@ const ItemsList = ({ [holding.id, records, isItemsDragSelected, selectItemsForDrag], ); const formatter = useMemo( - () => getFormatter(intl, locationsById, holdingsMapById, selectItemsForDrag, isItemsDragSelected), + () => getFormatter( + intl, + locationsById, + holdingsMapById, + selectItemsForDrag, + isItemsDragSelected, + isBarcodeAsHotlink, + tenantId, + ), [holdingsMapById, selectItemsForDrag, isItemsDragSelected], ); const rowProps = useMemo(() => ({ @@ -259,6 +273,8 @@ ItemsList.propTypes = { isItemsDragSelected: PropTypes.func.isRequired, getDraggingItems: PropTypes.func.isRequired, isFetching: PropTypes.bool, + tenantId: PropTypes.string, + isBarcodeAsHotlink: PropTypes.bool, }; ItemsList.defaultProps = { diff --git a/src/Instance/ItemsList/ItemsListContainer.js b/src/Instance/ItemsList/ItemsListContainer.js index ded0c52cf..8ead5a86b 100644 --- a/src/Instance/ItemsList/ItemsListContainer.js +++ b/src/Instance/ItemsList/ItemsListContainer.js @@ -10,7 +10,7 @@ import { isEmpty } from 'lodash'; import DnDContext from '../DnDContext'; import ItemsList from './ItemsList'; -import useHoldingItemsQuery from '../../hooks/useHoldingItemsQuery'; +import { useHoldingItemsQuery } from '../../hooks'; import { DEFAULT_ITEM_TABLE_SORTBY_FIELD } from '../../constants'; @@ -19,6 +19,7 @@ const ItemsListContainer = ({ holding, draggable, droppable, + isBarcodeAsHotlink, }) => { const { selectItemsForDrag, @@ -62,6 +63,8 @@ const ItemsListContainer = ({ draggable={draggable} droppable={droppable} isFetching={isFetching} + isBarcodeAsHotlink={isBarcodeAsHotlink} + tenantId={tenantId} /> ); }; @@ -71,6 +74,7 @@ ItemsListContainer.propTypes = { draggable: PropTypes.bool, droppable: PropTypes.bool, tenantId: PropTypes.string, + isBarcodeAsHotlink: PropTypes.bool, }; export default memo(ItemsListContainer); diff --git a/src/Instance/ItemsList/tests/ItemBarcode.test.js b/src/Instance/ItemsList/tests/ItemBarcode.test.js index 92bc7a47f..043b57cef 100644 --- a/src/Instance/ItemsList/tests/ItemBarcode.test.js +++ b/src/Instance/ItemsList/tests/ItemBarcode.test.js @@ -32,7 +32,8 @@ const itemProp = { const itemBarcodeProps = { item: itemProp, holdingId: 'testId1', - instanceId: 'testId2' + instanceId: 'testId2', + isBarcodeAsHotlink: true, }; const searchItem = qIndex => `?qindex=${qIndex}&query=${itemProp.barcode}`; @@ -41,11 +42,17 @@ const setupItemBarcode = ({ item, holdingId, instanceId, - history + history, + isBarcodeAsHotlink, }) => { const component = ( - + ); @@ -83,4 +90,23 @@ describe('', () => { expect(getByText(itemProp.barcode)).not.toHaveAttribute('data-test-highlighter-mark'); }); + + it('should render barcode as a hotlink', () => { + const history = getHistory(searchItem(QUERY_INDEXES.BARCODE)); + const { getByRole } = setupItemBarcode({ ...itemBarcodeProps, history }); + + expect(getByRole('button', { name: itemProp.barcode })).toBeInTheDocument(); + }); + + it('should render barcode as a plain text', () => { + const history = getHistory(searchItem(QUERY_INDEXES.BARCODE)); + const { queryByRole, getByText } = setupItemBarcode({ + ...itemBarcodeProps, + isBarcodeAsHotlink: false, + history, + }); + + expect(queryByRole('button', { name: itemProp.barcode })).not.toBeInTheDocument(); + expect(getByText(itemProp.barcode)).toBeInTheDocument(); + }); }); diff --git a/src/Item/CreateItem/CreateItem.js b/src/Item/CreateItem/CreateItem.js index a2b1b965a..d538783f4 100644 --- a/src/Item/CreateItem/CreateItem.js +++ b/src/Item/CreateItem/CreateItem.js @@ -3,16 +3,11 @@ import React, { useMemo, } from 'react'; import PropTypes from 'prop-types'; -import { - useHistory, - useLocation, -} from 'react-router-dom'; +import { useHistory } from 'react-router-dom'; import { FormattedMessage } from 'react-intl'; import { useStripes } from '@folio/stripes/core'; -import { - LoadingView, -} from '@folio/stripes/components'; +import { LoadingView } from '@folio/stripes/components'; import { useInstanceQuery, @@ -21,17 +16,26 @@ import { import ItemForm from '../../edit/items/ItemForm'; import useCallout from '../../hooks/useCallout'; import { useItemMutation } from '../hooks'; +import { switchAffiliation } from '../../utils'; const CreateItem = ({ referenceData, instanceId, holdingId, }) => { - const history = useHistory(); - const location = useLocation(); + const { + push, + location: { + search, + state: { + tenantTo, + tenantFrom, + }, + }, + } = useHistory(); - const { isLoading: isInstanceLoading, instance } = useInstanceQuery(instanceId); - const { isLoading: isHoldingLoading, holding } = useHolding(holdingId); + const { isLoading: isInstanceLoading, instance } = useInstanceQuery(instanceId, { tenantId: tenantTo }); + const { isLoading: isHoldingLoading, holding } = useHolding(holdingId, { tenantId: tenantTo }); const callout = useCallout(); const stripes = useStripes(); @@ -40,12 +44,16 @@ const CreateItem = ({ holdingsRecordId: holding.id, }), [holding.id]); - const onCancel = useCallback(() => { - history.push({ + const goBack = useCallback(() => { + push({ pathname: `/inventory/view/${instanceId}`, - search: location.search, + search, }); - }, [location.search, instanceId]); + }, [instanceId, search]); + + const onCancel = useCallback(() => { + switchAffiliation(stripes, tenantFrom, goBack); + }, [stripes, tenantFrom]); const onSuccess = useCallback(async (response) => { const { hrid } = await response.json(); diff --git a/src/Item/CreateItem/CreateItem.test.js b/src/Item/CreateItem/CreateItem.test.js index 8761679ce..f116a1b5d 100644 --- a/src/Item/CreateItem/CreateItem.test.js +++ b/src/Item/CreateItem/CreateItem.test.js @@ -1,8 +1,9 @@ import '../../../test/jest/__mock__'; -import { MemoryRouter } from 'react-router-dom'; +import { Router } from 'react-router-dom'; import { render, screen } from '@folio/jest-config-stripes/testing-library/react'; import { QueryClient, QueryClientProvider } from 'react-query'; +import { createMemoryHistory } from 'history'; import { instance } from '../../../test/fixtures/instance'; import { @@ -19,6 +20,16 @@ jest.mock('../../common/hooks', () => ({ useHolding: jest.fn().mockReturnValue({ holding: {}, isLoading: false }), })); +const history = createMemoryHistory(); +history.location = { + pathname: '/testPathName', + search: '?filters=test1', + state: { + tenantTo: 'testTenantToId', + tenantFrom: 'testTenantFromId', + } +}; + const defaultProps = { instanceId: instance.id, holdingId: 'holdingId', @@ -28,11 +39,11 @@ const defaultProps = { const queryClient = new QueryClient(); const wrapper = ({ children }) => ( - + {children} - + ); const renderCreateItem = (props = {}) => render( diff --git a/src/View.css b/src/View.css index 78d48392e..3ce243665 100644 --- a/src/View.css +++ b/src/View.css @@ -13,4 +13,22 @@ .boundWithIcon { background-color: transparent !important; margin-left: var(--gutter-static-one-third); -} \ No newline at end of file +} + +/* Reset default behaviour for item hotlink */ +.linkWithoutBorder { + &:hover, + &:focus { + outline-style: none !important; + } +} + +.linkWithoutBorder:hover::before { + background: transparent; +} + +.linkWithoutBorder:focus::before { + border: none; + background: transparent; + box-shadow: none; +} diff --git a/src/ViewHoldingsRecord.js b/src/ViewHoldingsRecord.js index 9815dd81c..35c0bc150 100644 --- a/src/ViewHoldingsRecord.js +++ b/src/ViewHoldingsRecord.js @@ -52,6 +52,7 @@ import { getSortedNotes, handleKeyCommand, getDate, + switchAffiliation, } from './utils'; import withLocation from './withLocation'; import { @@ -80,6 +81,7 @@ class ViewHoldingsRecord extends React.Component { path: 'holdings-storage/holdings/:{holdingsrecordid}', resourceShouldRefresh: false, accumulate: true, + tenant: '!{location.state.tenantTo}' }, items: { type: 'okapi', @@ -95,6 +97,7 @@ class ViewHoldingsRecord extends React.Component { type: 'okapi', path: 'inventory/instances/:{id}', accumulate: true, + tenant: '!{location.state.tenantTo}' }, tagSettings: { type: 'okapi', @@ -476,6 +479,7 @@ class ViewHoldingsRecord extends React.Component { referenceTables, goTo, stripes, + location: { state: { tenantFrom } }, } = this.props; const { instance } = this.state; @@ -722,7 +726,7 @@ class ViewHoldingsRecord extends React.Component { updatedDate: getDate(holdingsRecord?.metadata?.updatedDate), })} dismissible - onClose={this.onClose} + onClose={() => switchAffiliation(stripes, tenantFrom, this.onClose)} actionMenu={this.getPaneHeaderActionMenu} > @@ -1086,6 +1090,7 @@ ViewHoldingsRecord.propTypes = { connect: PropTypes.func.isRequired, hasPerm: PropTypes.func.isRequired, hasInterface: PropTypes.func.isRequired, + okapi: PropTypes.object.isRequired, }).isRequired, resources: PropTypes.shape({ instances1: PropTypes.object, diff --git a/src/ViewHoldingsRecord.test.js b/src/ViewHoldingsRecord.test.js index 1f5f4459c..5287c07bb 100644 --- a/src/ViewHoldingsRecord.test.js +++ b/src/ViewHoldingsRecord.test.js @@ -19,9 +19,13 @@ import { import ViewHoldingsRecord from './ViewHoldingsRecord'; - jest.mock('./withLocation', () => jest.fn(c => c)); +jest.mock('./common', () => ({ + ...jest.requireActual('./common'), + useTenantKy: jest.fn(), +})); + const spyOncollapseAllSections = jest.spyOn(require('@folio/stripes/components'), 'collapseAllSections'); const spyOnexpandAllSections = jest.spyOn(require('@folio/stripes/components'), 'expandAllSections'); @@ -77,7 +81,11 @@ const defaultProps = { }, location: { search: '/', - pathname: 'pathname' + pathname: 'pathname', + state: { + tenantTo: 'testTenantToId', + tenantFrom: 'testTenantFromId', + } }, }; @@ -108,7 +116,7 @@ describe('ViewHoldingsRecord actions', () => { it('should close view holding page', async () => { renderViewHoldingsRecord(); fireEvent.click(await screen.findByRole('button', { name: 'confirm' })); - expect(defaultProps.history.push).toBeCalled(); + expect(defaultProps.history.push).toHaveBeenCalled(); }); it('should translate to edit holding form page', async () => { diff --git a/src/ViewInstance.js b/src/ViewInstance.js index f67977944..3aa636308 100644 --- a/src/ViewInstance.js +++ b/src/ViewInstance.js @@ -33,6 +33,8 @@ import makeConnectedInstance from './ConnectedInstance'; import withLocation from './withLocation'; import InstancePlugin from './components/InstancePlugin'; import { + isUserInConsortiumMode, + getUserTenantsPermissions, handleKeyCommand, isInstanceShadowCopy, isMARCSource, @@ -186,6 +188,7 @@ class ViewInstance extends React.Component { isNewOrderModalOpen: false, afterCreate: false, instancesQuickExportInProgress: false, + userTenantPermissions: [], }; this.instanceId = null; this.intervalId = null; @@ -196,7 +199,10 @@ class ViewInstance extends React.Component { } componentDidMount() { - const { selectedInstance } = this.props; + const { + selectedInstance, + stripes, + } = this.props; const isMARCSourceRecord = isMARCSource(selectedInstance?.source); if (isMARCSourceRecord) { @@ -204,6 +210,10 @@ class ViewInstance extends React.Component { } this.setTlrSettings(); + + if (isUserInConsortiumMode(stripes)) { + this.getCurrentTenantPermissions(); + } } componentDidUpdate(prevProps) { @@ -249,6 +259,15 @@ class ViewInstance extends React.Component { clearInterval(this.intervalId); } + getCurrentTenantPermissions = () => { + const { + stripes, + stripes: { user: { user: { tenants } } }, + } = this.props; + + getUserTenantsPermissions(stripes, tenants).then(userTenantPermissions => this.setState({ userTenantPermissions })); + } + getMARCRecord = () => { const { mutator } = this.props; mutator.marcRecord.GET() @@ -917,6 +936,7 @@ class ViewInstance extends React.Component { instance={instance} tagsEnabled={tagsEnabled} ref={this.accordionStatusRef} + userTenantPermissions={this.state.userTenantPermissions} isLoading={isInstanceLoading} isShared={isShared} > @@ -1061,8 +1081,16 @@ ViewInstance.propTypes = { hasPerm: PropTypes.func.isRequired, locale: PropTypes.string.isRequired, logger: PropTypes.object.isRequired, + user: PropTypes.shape({ + user: PropTypes.shape({ + tenants: PropTypes.arrayOf(PropTypes.object), + }).isRequired + }).isRequired, + }).isRequired, + okapi: PropTypes.shape({ + tenant: PropTypes.string.isRequired, + token: PropTypes.string.isRequired }).isRequired, - okapi: PropTypes.object.isRequired, tagsEnabled: PropTypes.bool, updateLocation: PropTypes.func.isRequired, }; diff --git a/src/ViewInstance.test.js b/src/ViewInstance.test.js index 558b74a27..188eb8000 100644 --- a/src/ViewInstance.test.js +++ b/src/ViewInstance.test.js @@ -32,6 +32,8 @@ import { translationsProperties, } from '../test/jest/helpers'; +import * as utils from './utils'; + jest.mock('./components/ImportRecordModal/ImportRecordModal', () => (props) => { const { isOpen, handleSubmit, handleCancel } = props; if (isOpen) { @@ -68,10 +70,16 @@ jest.mock('./common/hooks', () => ({ ...jest.requireActual('./common/hooks'), useTenantKy: jest.fn(), })); +jest.mock('react-beautiful-dnd', () => ({ + ...jest.requireActual('react-beautiful-dnd'), + Draggable: jest.fn(() =>
Press space bar to start a drag
), +})); const spyOncollapseAllSections = jest.spyOn(require('@folio/stripes/components'), 'collapseAllSections'); const spyOnexpandAllSections = jest.spyOn(require('@folio/stripes/components'), 'expandAllSections'); +const spyOnGetUserTenantsPermissions = jest.spyOn(utils, 'getUserTenantsPermissions'); + const location = { pathname: '/testPathName', search: '?filters=test1&query=test2&sort=test3&qindex=test', @@ -132,7 +140,10 @@ const mockStripes = { log: jest.fn() }, okapi: { tenant: 'diku' }, - user: { user: {} }, + user: { + user: {}, + tenants: ['testTenantId'] + }, }; const defaultProp = { centralTenantPermissions: [], @@ -257,11 +268,22 @@ const checkActionItemExists = (actionItemName) => { expect(screen.getByRole('button', { name: actionItemName })).toBeInTheDocument(); }; +global.fetch = jest.fn(); + describe('ViewInstance', () => { beforeEach(() => { jest.clearAllMocks(); StripesConnectedInstance.prototype.instance.mockImplementation(() => instance); checkIfUserInCentralTenant.mockReturnValue(false); + spyOnGetUserTenantsPermissions.mockResolvedValueOnce([{ + tenantId: 'testTenantId', + permissionNames: ['test permission 1'], + }]); + global.fetch + .mockResolvedValueOnce({ + ok: true, + json: async () => ({}), + }); useStripes().hasInterface.mockReturnValue(true); }); it('should display action menu items', () => { @@ -399,11 +421,14 @@ describe('ViewInstance', () => { checkActionItemExists('Move items within an instance'); }); - it('"Move items within an instance" button to be clicked', () => { - renderViewInstance(); - fireEvent.click(screen.getByRole('button', { name: 'Actions' })); - fireEvent.click(screen.getByRole('button', { name: 'Move items within an instance' })); - expect(renderViewInstance()).toBeTruthy(); + describe('when click "Move items within an instance" button', () => { + it('should render component for dragging', () => { + const { getByText } = renderViewInstance(); + fireEvent.click(screen.getByRole('button', { name: 'Actions' })); + fireEvent.click(screen.getByRole('button', { name: 'Move items within an instance' })); + + expect(getByText(/Press space bar to start a drag/i)).toBeInTheDocument(); + }); }); describe('when user is in central tenant', () => { @@ -629,11 +654,14 @@ describe('ViewInstance', () => { checkActionItemExists('Export instance (MARC)'); }); - it('"Export instance (MARC)" button to be clicked', () => { - renderViewInstance(); - fireEvent.click(screen.getByRole('button', { name: 'Actions' })); - fireEvent.click(screen.getByRole('button', { name: 'Export instance (MARC)' })); - expect(renderViewInstance()).toBeTruthy(); + describe('when click "Export instance (MARC)" button', () => { + it('should call function to export instance', () => { + renderViewInstance(); + fireEvent.click(screen.getByRole('button', { name: 'Actions' })); + fireEvent.click(screen.getByRole('button', { name: 'Export instance (MARC)' })); + + expect(defaultProp.mutator.quickExport.POST).toHaveBeenCalled(); + }); }); }); describe('"New order" action item', () => { @@ -665,7 +693,12 @@ describe('ViewInstance', () => { const stripes = { ...defaultProp.stripes, okapi: { tenant: 'consortium' }, - user: { user: { consortium: { centralTenantId: 'consortium' } } }, + user: { + user: { + consortium: { centralTenantId: 'consortium' }, + tenants: ['testTenantId'], + }, + }, }; checkIfUserInCentralTenant.mockClear().mockReturnValue(true); @@ -710,7 +743,12 @@ describe('ViewInstance', () => { const stripes = { ...defaultProp.stripes, okapi: { tenant: 'consortium' }, - user: { user: { consortium: { centralTenantId: 'consortium' } } }, + user: { + user: { + consortium: { centralTenantId: 'consortium' }, + tenants: ['testTenantId'], + }, + }, }; renderViewInstance({ stripes }); diff --git a/src/common/hooks/useHolding/useHolding.js b/src/common/hooks/useHolding/useHolding.js index 6c47cee3a..19dba9eb0 100644 --- a/src/common/hooks/useHolding/useHolding.js +++ b/src/common/hooks/useHolding/useHolding.js @@ -1,9 +1,11 @@ import { useQuery } from 'react-query'; -import { useOkapiKy, useNamespace } from '@folio/stripes/core'; +import { useNamespace } from '@folio/stripes/core'; -const useHolding = (holdingId) => { - const ky = useOkapiKy(); +import useTenantKy from '../useTenantKy'; + +const useHolding = (holdingId, { tenantId = '' } = {}) => { + const ky = useTenantKy({ tenantId }); const [namespace] = useNamespace({ key: 'holding' }); const { isLoading, data: holding = {} } = useQuery( diff --git a/src/constants.js b/src/constants.js index 118aaf841..534d4d280 100644 --- a/src/constants.js +++ b/src/constants.js @@ -631,6 +631,8 @@ export const SOURCE_VALUES = { export const CONSORTIUM_PREFIX = 'CONSORTIUM-'; export const OKAPI_TENANT_HEADER = 'X-Okapi-Tenant'; +export const OKAPI_TOKEN_HEADER = 'X-Okapi-Token'; +export const CONTENT_TYPE_HEADER = 'Content-Type'; export const DEFAULT_ITEM_TABLE_SORTBY_FIELD = 'barcode'; diff --git a/src/hooks/useChunkedCQLFetch.js b/src/hooks/useChunkedCQLFetch.js index 34ede67ce..56406f83c 100644 --- a/src/hooks/useChunkedCQLFetch.js +++ b/src/hooks/useChunkedCQLFetch.js @@ -3,16 +3,17 @@ import { useQueries } from 'react-query'; import { chunk } from 'lodash'; -import { useOkapiKy } from '@folio/stripes/core'; +import { useTenantKy } from '../common'; // When fetching from a potentially large list of items, // make sure to chunk the request to avoid hitting limits. const useChunkedCQLFetch = ({ endpoint, // endpoint to hit to fetch items ids, // List of ids to fetch - reduceFunction // Function to reduce fetched objects at the end into single array + reduceFunction, // Function to reduce fetched objects at the end into single array + tenantId, }) => { - const ky = useOkapiKy(); + const ky = useTenantKy({ tenantId }); const CONCURRENT_REQUESTS = 5; // Number of requests to make concurrently const STEP_SIZE = 60; // Number of ids to request for per concurrent request diff --git a/src/routes/ItemRoute.js b/src/routes/ItemRoute.js index 60b186e86..f3235ab5e 100644 --- a/src/routes/ItemRoute.js +++ b/src/routes/ItemRoute.js @@ -13,6 +13,7 @@ import withLocation from '../withLocation'; import { ItemView } from '../views'; import { PaneLoading } from '../components'; import { DataContext } from '../contexts'; +import { switchAffiliation } from '../utils'; const getRequestsPath = `circulation/requests?query=(itemId==:{itemid}) and status==(${requestsStatusString}) sortby requestDate desc&limit=1`; @@ -24,6 +25,7 @@ class ItemRoute extends React.Component { path: 'inventory/items/:{itemid}', POST: { path: 'inventory/items' }, resourceShouldRefresh: true, + tenant: '!{location.state.tenantTo}', }, markItemAsWithdrawn: { type: 'okapi', @@ -100,6 +102,7 @@ class ItemRoute extends React.Component { holdingsRecords: { type: 'okapi', path: 'holdings-storage/holdings/:{holdingsrecordid}', + tenant: '!{location.state.tenantTo}', }, instanceRecords: { type: 'okapi', @@ -155,19 +158,26 @@ class ItemRoute extends React.Component { }, }); - onClose = () => { + goBack = () => { const { - goTo, - match: { - params: { id }, - }, - location: { pathname, search }, + match: { params: { id } }, + location: { search }, + history, } = this.props; - // extract instance url - const [path] = pathname.match(new RegExp(`(.*)${id}`)); + history.push({ + pathname: `/inventory/view/${id}`, + search, + }); + } + + onClose = () => { + const { + stripes, + location: { state: { tenantFrom } }, + } = this.props; - goTo(`${path}${search}`); + switchAffiliation(stripes, tenantFrom, this.goBack); } isLoading = () => { @@ -212,6 +222,9 @@ ItemRoute.propTypes = { match: PropTypes.object, location: PropTypes.object, resources: PropTypes.object, + stripes: PropTypes.object, + tenantFrom: PropTypes.string, + history: PropTypes.object, }; export default flowRight( diff --git a/src/utils.js b/src/utils.js index 19e11fcc3..0de843be4 100644 --- a/src/utils.js +++ b/src/utils.js @@ -21,6 +21,10 @@ import { } from 'lodash'; import moment from 'moment'; +import { + updateTenant, + validateUser, +} from '@folio/stripes/core'; import { FormattedUTCDate } from '@folio/stripes/components'; import { @@ -34,6 +38,9 @@ import { ERROR_TYPES, SINGLE_ITEM_QUERY_TEMPLATES, CONSORTIUM_PREFIX, + OKAPI_TENANT_HEADER, + CONTENT_TYPE_HEADER, + OKAPI_TOKEN_HEADER, } from './constants'; export const areAllFieldsEmpty = fields => fields.every(item => (isArray(item) @@ -782,3 +789,64 @@ export const isMARCSource = (source) => { export const isUserInConsortiumMode = stripes => stripes.hasInterface('consortia'); export const isInstanceShadowCopy = (source) => [`${CONSORTIUM_PREFIX}FOLIO`, `${CONSORTIUM_PREFIX}MARC`].includes(source); + +export const getUserTenantsPermissions = (stripes, tenants = []) => { + const { + user: { user: { id } }, + okapi: { + url, + token, + } + } = stripes; + const userTenantIds = tenants.map(tenant => tenant.id || tenant); + + const promises = userTenantIds.map(async (tenantId) => { + const result = await fetch(`${url}/perms/users/${id}/permissions?full=true&indexField=userId`, { + headers: { + [OKAPI_TENANT_HEADER]: tenantId, + [CONTENT_TYPE_HEADER]: 'application/json', + ...(token && { [OKAPI_TOKEN_HEADER]: token }), + }, + credentials: 'include', + }); + + const json = await result.json(); + + return { tenantId, ...json }; + }); + + return Promise.all(promises); +}; + +export const hasMemberTenantPermission = (permissionName, tenantId, permissions = []) => { + const tenantPermissions = permissions?.find(permission => permission?.tenantId === tenantId)?.permissionNames || []; + + const hasPermission = tenantPermissions?.some(tenantPermission => tenantPermission?.permissionName === permissionName); + + if (!hasPermission) { + return tenantPermissions.some(tenantPermission => tenantPermission.subPermissions.includes(permissionName)); + } + + return hasPermission; +}; + +export const switchAffiliation = async (stripes, tenantId, move) => { + if (stripes.okapi.tenant !== tenantId) { + await updateTenant(stripes.okapi, tenantId); + + validateUser( + stripes.okapi.url, + stripes.store, + tenantId, + { + token: stripes.okapi.token, + user: stripes.user.user, + perms: stripes.user.perms, + }, + ); + + move(); + } else { + move(); + } +}; diff --git a/src/utils.test.js b/src/utils.test.js index e225ca34e..1c8fe72d8 100644 --- a/src/utils.test.js +++ b/src/utils.test.js @@ -1,12 +1,18 @@ import '../test/jest/__mock__'; import { FormattedMessage } from 'react-intl'; + +import { updateTenant } from '@folio/stripes/core'; + +import buildStripes from '../test/jest/__mock__/stripesCore.mock'; + import { validateRequiredField, validateFieldLength, validateNumericField, validateAlphaNumericField, getQueryTemplate, + switchAffiliation, } from './utils'; import { browseModeOptions } from './constants'; @@ -144,3 +150,32 @@ describe('getQueryTemplate', () => { }); }); +describe('switchAffiliation', () => { + beforeEach(() => { + jest.clearAllMocks(); + }); + + const moveMock = jest.fn(); + const stripes = buildStripes({ + okapi: { + tenant: 'college', + token: 'testToken', + }, + }); + + describe('when current tenant is the same as tenant to switch', () => { + it('should only move to the next page', () => { + switchAffiliation(stripes, 'college', moveMock); + + expect(moveMock).toHaveBeenCalled(); + }); + }); + + describe('when current tenant is not the same as tenant to switch', () => { + it('should switch affiliation', () => { + switchAffiliation(stripes, 'university', moveMock); + + expect(updateTenant).toHaveBeenCalled(); + }); + }); +}); diff --git a/src/views/ItemView.js b/src/views/ItemView.js index eff5f68e3..a613e1c90 100644 --- a/src/views/ItemView.js +++ b/src/views/ItemView.js @@ -753,7 +753,7 @@ class ItemView extends React.Component { } value={checkIfElementIsEmpty(itemLocation.effectiveLocation.name)} - subValue={!itemLocation.effectiveLocation.isActive && + subValue={!itemLocation.effectiveLocation?.isActive && } data-testid="item-effective-location" @@ -1335,7 +1335,7 @@ class ItemView extends React.Component { } value={checkIfElementIsEmpty(holdingLocation.permanentLocation.name)} - subValue={!holdingLocation.permanentLocation.isActive && + subValue={!holdingLocation.permanentLocation?.isActive && } data-testid="holding-permanent-location" @@ -1345,7 +1345,7 @@ class ItemView extends React.Component { } value={checkIfElementIsEmpty(holdingLocation.temporaryLocation.name)} - subValue={holdingLocation.temporaryLocation.isActive === false && + subValue={holdingLocation.temporaryLocation?.isActive === false && } data-testid="holding-temporary-location" @@ -1371,7 +1371,7 @@ class ItemView extends React.Component { } value={checkIfElementIsEmpty(itemLocation.permanentLocation.name)} - subValue={itemLocation.permanentLocation.isActive === false && + subValue={itemLocation.permanentLocation?.isActive === false && } data-testid="item-permanent-location" @@ -1381,7 +1381,7 @@ class ItemView extends React.Component { } value={checkIfElementIsEmpty(itemLocation.temporaryLocation.name)} - subValue={itemLocation.temporaryLocation.isActive === false && + subValue={itemLocation.temporaryLocation?.isActive === false && } data-testid="item-temporary-location" diff --git a/test/jest/__mock__/stripesCore.mock.js b/test/jest/__mock__/stripesCore.mock.js index 15b036546..a9ad0a2b4 100644 --- a/test/jest/__mock__/stripesCore.mock.js +++ b/test/jest/__mock__/stripesCore.mock.js @@ -101,6 +101,10 @@ const mockStripesCore = { checkIfUserInMemberTenant: jest.fn(() => true), checkIfUserInCentralTenant: jest.fn(() => false), + + updateTenant: jest.fn(() => {}), + + validateUser: jest.fn(() => {}), }; jest.mock('@folio/stripes/core', () => ({ From 4054062749d05fa4de89deae72b23e3c1ea4053e Mon Sep 17 00:00:00 2001 From: Oleksandr Hladchenko <85172747+OleksandrHladchenko1@users.noreply.github.com> Date: Mon, 23 Oct 2023 16:24:08 +0300 Subject: [PATCH 5/7] UIIN-2634: Remove error when search for an item (#2318) * UIIN-2634: Remove error when search for an item * UIIN-2634: Fix tests --- CHANGELOG.md | 1 + src/ViewHoldingsRecord.js | 3 ++- src/components/InstancesList/InstancesList.js | 18 +++++++++++++++--- .../InstancesList/InstancesList.test.js | 1 + src/routes/ItemRoute.js | 3 ++- 5 files changed, 21 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b58071dda..d2f21a30f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ * Instance. Series heading has vanished in detailed view. Fixes UIIN-2601. * Remove error message after switch from Instance Edit screen to another app. Fixes UIIN-2600. * Enable/disable consortial holdings/item actions based on User permissions. Refs UIIN-2452. +* User receives an error when searching for an item in the Inventory app. Fixes UIIN-2634. ## [10.0.0](https://github.com/folio-org/ui-inventory/tree/v10.0.0) (2023-10-13) [Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.12...v10.0.0) diff --git a/src/ViewHoldingsRecord.js b/src/ViewHoldingsRecord.js index 35c0bc150..8684f4f6d 100644 --- a/src/ViewHoldingsRecord.js +++ b/src/ViewHoldingsRecord.js @@ -479,9 +479,10 @@ class ViewHoldingsRecord extends React.Component { referenceTables, goTo, stripes, - location: { state: { tenantFrom } }, + location, } = this.props; const { instance } = this.state; + const tenantFrom = location?.state?.tenantFrom || stripes.okapi.tenant; if (this.isAwaitingResource()) return ; diff --git a/src/components/InstancesList/InstancesList.js b/src/components/InstancesList/InstancesList.js index afe57ce1e..49a468b63 100644 --- a/src/components/InstancesList/InstancesList.js +++ b/src/components/InstancesList/InstancesList.js @@ -16,6 +16,7 @@ import { import saveAs from 'file-saver'; import moment from 'moment'; import classnames from 'classnames'; +import { stringify } from 'query-string'; import { Pluggable, @@ -134,6 +135,7 @@ class InstancesList extends React.Component { }), stripes: PropTypes.object.isRequired, history: PropTypes.shape({ + push: PropTypes.func, listen: PropTypes.func, replace: PropTypes.func, }), @@ -956,8 +958,9 @@ class InstancesList extends React.Component { const { parentResources, parentMutator: { itemsByQuery }, - goTo, getParams, + stripes, + history, } = this.props; const { query, qindex } = parentResources?.query ?? {}; const { searchInProgress } = this.state; @@ -973,7 +976,10 @@ class InstancesList extends React.Component { } itemsByQuery.reset(); - const items = await itemsByQuery.GET({ params: { query: itemQuery } }); + const items = await itemsByQuery.GET({ + params: { query: itemQuery }, + tenant: stripes.okapi.tenant, + }); this.setState({ searchInProgress: false }); @@ -984,7 +990,13 @@ class InstancesList extends React.Component { } const { id, holdingsRecordId } = items[0]; - goTo(`/inventory/view/${instance.id}/${holdingsRecordId}/${id}`, getParams()); + const search = stringify(getParams()); + + history.push({ + pathname: `/inventory/view/${instance.id}/${holdingsRecordId}/${id}`, + search, + state: { tenantTo: stripes.okapi.tenant }, + }); return null; } diff --git a/src/components/InstancesList/InstancesList.test.js b/src/components/InstancesList/InstancesList.test.js index ea5f0f3d3..2c88470d9 100644 --- a/src/components/InstancesList/InstancesList.test.js +++ b/src/components/InstancesList/InstancesList.test.js @@ -559,6 +559,7 @@ describe('InstancesList', () => { params: { query: `${option}=="${_query}"`, }, + tenant: 'diku', }); }); }); diff --git a/src/routes/ItemRoute.js b/src/routes/ItemRoute.js index f3235ab5e..c55ec7d76 100644 --- a/src/routes/ItemRoute.js +++ b/src/routes/ItemRoute.js @@ -174,8 +174,9 @@ class ItemRoute extends React.Component { onClose = () => { const { stripes, - location: { state: { tenantFrom } }, + location, } = this.props; + const tenantFrom = location?.state?.tenantFrom || stripes.okapi.tenant; switchAffiliation(stripes, tenantFrom, this.goBack); } From f0c4b11848fc19da6abdb8de73cdde863db371ff Mon Sep 17 00:00:00 2001 From: Mariia Aloshyna <55138456+mariia-aloshyna@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:11:19 +0300 Subject: [PATCH 6/7] UIIN-2635: Create new instance success toast no longer shows the instance HRID (#2317) --- CHANGELOG.md | 1 + src/ViewInstance.js | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index d2f21a30f..b686dab07 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -16,6 +16,7 @@ * Remove error message after switch from Instance Edit screen to another app. Fixes UIIN-2600. * Enable/disable consortial holdings/item actions based on User permissions. Refs UIIN-2452. * User receives an error when searching for an item in the Inventory app. Fixes UIIN-2634. +* Create new instance success toast no longer shows the instance HRID. Fixes UIIN-2635. ## [10.0.0](https://github.com/folio-org/ui-inventory/tree/v10.0.0) (2023-10-13) [Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.12...v10.0.0) diff --git a/src/ViewInstance.js b/src/ViewInstance.js index 3aa636308..beeed1f4d 100644 --- a/src/ViewInstance.js +++ b/src/ViewInstance.js @@ -960,9 +960,9 @@ class ViewInstance extends React.Component { - {this.state.afterCreate && + {this.state.afterCreate && !isEmpty(instance) && } + message={} /> } From 33589f72ccdc7418f83d43ce9574b0387daf48cc Mon Sep 17 00:00:00 2001 From: Mariia Aloshyna <55138456+mariia-aloshyna@users.noreply.github.com> Date: Mon, 23 Oct 2023 17:32:10 +0300 Subject: [PATCH 7/7] UIIN-2628: Optimistic locking message not working for instances in non-consortial tenant (#2316) --- CHANGELOG.md | 1 + src/Instance/InstanceEdit/InstanceEdit.js | 10 ++++------ src/hooks/useInstanceMutation/useInstanceMutation.js | 6 +++--- 3 files changed, 8 insertions(+), 9 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index b686dab07..a2e12fcb6 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,7 @@ * Enable/disable consortial holdings/item actions based on User permissions. Refs UIIN-2452. * User receives an error when searching for an item in the Inventory app. Fixes UIIN-2634. * Create new instance success toast no longer shows the instance HRID. Fixes UIIN-2635. +* Optimistic locking message not working for instances in non-consortial tenant. Fixes UIIN-2628. ## [10.0.0](https://github.com/folio-org/ui-inventory/tree/v10.0.0) (2023-10-13) [Full Changelog](https://github.com/folio-org/ui-inventory/compare/v9.4.12...v10.0.0) diff --git a/src/Instance/InstanceEdit/InstanceEdit.js b/src/Instance/InstanceEdit/InstanceEdit.js index ed10ba12c..4eb34cae4 100644 --- a/src/Instance/InstanceEdit/InstanceEdit.js +++ b/src/Instance/InstanceEdit/InstanceEdit.js @@ -72,22 +72,20 @@ const InstanceEdit = ({ }, [callout, goBack]); const onError = async error => { - const parsedError = await parseHttpError(error); + const response = await error.response; + const parsedError = await parseHttpError(response); setHttpError(parsedError); }; const isMemberTenant = checkIfUserInMemberTenant(stripes); const tenantId = (isMemberTenant && instance?.shared) ? stripes.user.user.consortium.centralTenantId : stripes.okapi.tenant; - const { mutateInstance } = useInstanceMutation({ - options: { onSuccess, onError }, - tenantId, - }); + const { mutateInstance } = useInstanceMutation({ tenantId }); const onSubmit = useCallback((initialInstance) => { const updatedInstance = marshalInstance(initialInstance, identifierTypesByName); - return mutateInstance(updatedInstance); + return mutateInstance(updatedInstance, { onSuccess, onError }); }, [mutateInstance]); if (isInstanceLoading) return ; diff --git a/src/hooks/useInstanceMutation/useInstanceMutation.js b/src/hooks/useInstanceMutation/useInstanceMutation.js index 7ed3c2af3..082e4548b 100644 --- a/src/hooks/useInstanceMutation/useInstanceMutation.js +++ b/src/hooks/useInstanceMutation/useInstanceMutation.js @@ -5,18 +5,18 @@ import { useTenantKy } from '../../common'; const useInstanceMutation = ({ tenantId, options = {} }) => { const ky = useTenantKy({ tenantId }); - const { mutateAsync } = useMutation({ + const { mutate } = useMutation({ mutationFn: (instance) => { const kyMethod = instance.id ? 'put' : 'post'; const kyPath = instance.id ? `inventory/instances/${instance.id}` : 'inventory/instances'; - return ky[kyMethod](kyPath, { json: instance }); + return ky[kyMethod](kyPath, { json: instance }).json(); }, ...options, }); return { - mutateInstance: mutateAsync, + mutateInstance: mutate, }; };