From 09b0d1bdc9e8b46557f98f8dd43fdef9742d0620 Mon Sep 17 00:00:00 2001
From: Mariia Aloshyna <55138456+mariia-aloshyna@users.noreply.github.com>
Date: Thu, 26 Oct 2023 16:39:11 +0300
Subject: [PATCH 1/2] UIIN-2617: Add immediate warning message when a local
instance is shared (#2321)
---
CHANGELOG.md | 1 +
.../InstanceDetails/InstanceDetails.js | 97 ++++++++++++-------
.../InstanceDetails/InstanceDetails.test.js | 7 ++
src/ViewInstance.js | 16 ++-
src/ViewInstanceWrapper.js | 3 +-
src/common/hooks/useInstance.js | 6 +-
translations/ui-inventory/en.json | 1 +
7 files changed, 87 insertions(+), 44 deletions(-)
diff --git a/CHANGELOG.md b/CHANGELOG.md
index a2e12fcb6..e89d912fc 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -18,6 +18,7 @@
* 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.
+* Add immediate warning message when a local instance is shared. Refs UIIN-2617.
## [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/InstanceDetails.js b/src/Instance/InstanceDetails/InstanceDetails.js
index b903fc236..954d66086 100644
--- a/src/Instance/InstanceDetails/InstanceDetails.js
+++ b/src/Instance/InstanceDetails/InstanceDetails.js
@@ -72,6 +72,7 @@ const InstanceDetails = forwardRef(({
userTenantPermissions,
isShared,
isLoading,
+ isInstanceSharing,
...rest
}, ref) => {
const intl = useIntl();
@@ -85,12 +86,15 @@ const InstanceDetails = forwardRef(({
const accordionState = useMemo(() => getAccordionState(instance, accordions), [instance]);
const [helperApp, setHelperApp] = useState();
+ const isBasicPane = isInstanceSharing || isLoading;
const tags = instance?.tags?.tagList;
const isUserInCentralTenant = checkIfUserInCentralTenant(stripes);
const canCreateHoldings = stripes.hasPerm('ui-inventory.holdings.edit');
const detailsLastMenu = useMemo(() => {
+ if (isBasicPane) return null;
+
return (
{
@@ -106,31 +110,15 @@ const InstanceDetails = forwardRef(({
}
);
- }, [tagsEnabled, tags, intl]);
-
- if (isLoading) {
- return (
- }
- dismissible
- onClose={onClose}
- >
-
-
-
-
- );
- }
-
- const isConsortialHoldingsVisible = instance?.shared || isInstanceShadowCopy(instance?.source);
+ }, [isBasicPane, tagsEnabled, tags, intl]);
+ const detailsActionMenu = useMemo(
+ () => (isBasicPane ? null : actionMenu),
+ [isBasicPane, actionMenu],
+ );
const renderPaneTitle = () => {
+ if (isBasicPane) return intl.formatMessage({ id: 'ui-inventory.edit' });
+
const isInstanceShared = Boolean(isShared || isInstanceShadowCopy(instance?.source));
return (
@@ -146,6 +134,8 @@ const InstanceDetails = forwardRef(({
};
const renderPaneSubtitle = () => {
+ if (isBasicPane) return null;
+
return (
- }
- paneTitle={renderPaneTitle()}
- paneSub={renderPaneSubtitle()}
- dismissible
- onClose={onClose}
- actionMenu={actionMenu}
- defaultWidth="fill"
- lastMenu={detailsLastMenu}
- >
+ const renderDetails = () => {
+ if (isInstanceSharing) {
+ return (
+
+
+
+
+
+ );
+ }
+
+ if (isLoading) {
+ return (
+
+
+
+ );
+ }
+
+ const isConsortialHoldingsVisible = instance?.shared || isInstanceShadowCopy(instance?.source);
+
+ return (
+ <>
@@ -291,6 +293,25 @@ const InstanceDetails = forwardRef(({
/>
+ >
+ );
+ };
+
+ return (
+ <>
+ }
+ paneTitle={renderPaneTitle()}
+ paneSub={renderPaneSubtitle()}
+ actionMenu={detailsActionMenu}
+ lastMenu={detailsLastMenu}
+ dismissible
+ onClose={onClose}
+ defaultWidth="fill"
+ >
+ {renderDetails()}
{ helperApp && }
>
@@ -306,6 +327,7 @@ InstanceDetails.propTypes = {
tagsEnabled: PropTypes.bool,
userTenantPermissions: PropTypes.arrayOf(PropTypes.object),
isLoading: PropTypes.bool,
+ isInstanceSharing: PropTypes.bool,
isShared: PropTypes.bool,
};
@@ -313,6 +335,7 @@ InstanceDetails.defaultProps = {
instance: {},
tagsEnabled: false,
isLoading: false,
+ isInstanceSharing: false,
isShared: false,
};
diff --git a/src/Instance/InstanceDetails/InstanceDetails.test.js b/src/Instance/InstanceDetails/InstanceDetails.test.js
index daf483dcd..e8cd22f7e 100644
--- a/src/Instance/InstanceDetails/InstanceDetails.test.js
+++ b/src/Instance/InstanceDetails/InstanceDetails.test.js
@@ -112,6 +112,13 @@ describe('InstanceDetails', () => {
expect(screen.getByText('Instance relationship (analytics and bound-with)')).toBeInTheDocument();
});
+ it('should show a correct Warning message banner when instance sharing is in progress', () => {
+ renderInstanceDetails({ isInstanceSharing: true });
+
+ expect(screen.getByText('Sharing this local instance will take a few moments.' +
+ ' A success message and updated details will be displayed upon completion.')).toBeInTheDocument();
+ });
+
it('should show a correct Warning message banner when staff suppressed', () => {
const staffSuppressedInstance = {
...instance,
diff --git a/src/ViewInstance.js b/src/ViewInstance.js
index beeed1f4d..f687c918a 100644
--- a/src/ViewInstance.js
+++ b/src/ViewInstance.js
@@ -176,7 +176,7 @@ class ViewInstance extends React.Component {
this.log = logger.log.bind(logger);
this.state = {
- isLoading: false,
+ isInstanceSharing: false,
marcRecord: null,
findInstancePluginOpened: false,
isItemsMovement: false,
@@ -493,14 +493,14 @@ class ViewInstance extends React.Component {
this.setState({
isUnlinkAuthoritiesModalOpen: false,
isShareLocalInstanceModalOpen: false,
- isLoading: true
+ isInstanceSharing: true,
});
await this.waitForInstanceSharingComplete({ sourceTenantId, instanceIdentifier, instanceTitle });
})
.then(async () => {
await this.props.refetchInstance();
- this.setState({ isLoading: false });
+ this.setState({ isInstanceSharing: false });
this.calloutRef.current.sendCallout({
type: 'success',
message: ,
@@ -883,8 +883,12 @@ class ViewInstance extends React.Component {
canUseSingleRecordImport,
isCentralTenantPermissionsLoading,
isShared,
+ isLoading,
} = this.props;
- const { linkedAuthoritiesLength } = this.state;
+ const {
+ linkedAuthoritiesLength,
+ isInstanceSharing,
+ } = this.state;
const ci = makeConnectedInstance(this.props, stripes.logger);
const instance = ci.instance();
@@ -918,7 +922,7 @@ class ViewInstance extends React.Component {
handler: (e) => collapseAllSections(e, this.accordionStatusRef),
},
];
- const isInstanceLoading = this.state.isLoading || !instance || isCentralTenantPermissionsLoading;
+ const isInstanceLoading = isLoading || !instance || isCentralTenantPermissionsLoading;
const keyInStorageToHoldingsAccsState = ['holdings'];
return (
@@ -938,6 +942,7 @@ class ViewInstance extends React.Component {
ref={this.accordionStatusRef}
userTenantPermissions={this.state.userTenantPermissions}
isLoading={isInstanceLoading}
+ isInstanceSharing={isInstanceSharing}
isShared={isShared}
>
{
@@ -1093,6 +1098,7 @@ ViewInstance.propTypes = {
}).isRequired,
tagsEnabled: PropTypes.bool,
updateLocation: PropTypes.func.isRequired,
+ isLoading: PropTypes.bool,
};
export default flowRight(
diff --git a/src/ViewInstanceWrapper.js b/src/ViewInstanceWrapper.js
index 2fb8b2caa..170ed5ef1 100644
--- a/src/ViewInstanceWrapper.js
+++ b/src/ViewInstanceWrapper.js
@@ -17,7 +17,7 @@ const ViewInstanceWrapper = (props) => {
const userId = stripes?.user?.user?.id;
const centralTenantId = stripes.user.user?.consortium?.centralTenantId;
const consortiumId = stripes.user.user?.consortium?.id;
- const { instance, refetch } = useInstance(id);
+ const { instance, isLoading, refetch } = useInstance(id);
const isShared = Boolean(instance?.shared);
const tenantId = instance?.tenantId ?? stripes.okapi.tenant;
@@ -41,6 +41,7 @@ const ViewInstanceWrapper = (props) => {
consortiumId={consortiumId}
refetchInstance={refetch}
selectedInstance={instance}
+ isLoading={isLoading}
centralTenantPermissions={centralTenantPermissions}
isCentralTenantPermissionsLoading={isCentralTenantPermissionsLoading}
/>
diff --git a/src/common/hooks/useInstance.js b/src/common/hooks/useInstance.js
index b60cfc3bc..686408d17 100644
--- a/src/common/hooks/useInstance.js
+++ b/src/common/hooks/useInstance.js
@@ -9,7 +9,7 @@ const useInstance = (id) => {
const instanceTenantId = _instance?.tenantId;
const isShared = _instance?.shared;
- const { isLoading, instance: data, refetch } = useInstanceQuery(
+ const { isLoading: isInstanceLoading, instance: data, refetch } = useInstanceQuery(
id,
{ tenantId: instanceTenantId },
{ enabled: Boolean(id && !isSearchInstanceByIdLoading) }
@@ -23,6 +23,10 @@ const useInstance = (id) => {
}),
[data, isShared, instanceTenantId],
);
+ const isLoading = useMemo(
+ () => isSearchInstanceByIdLoading || isInstanceLoading,
+ [isSearchInstanceByIdLoading, isInstanceLoading],
+ );
return {
instance,
diff --git a/translations/ui-inventory/en.json b/translations/ui-inventory/en.json
index afa026c97..91e1ac580 100644
--- a/translations/ui-inventory/en.json
+++ b/translations/ui-inventory/en.json
@@ -798,6 +798,7 @@
"warning.instance.suppressedFromDiscovery": "Warning: Instance is marked suppressed from discovery",
"warning.instance.staffSuppressed": "Warning: Instance is marked staff suppressed",
"warning.instance.suppressedFromDiscoveryAndStaffSuppressed": "Warning: Instance is marked suppressed from discovery and staff suppressed",
+ "warning.instance.sharingLocalInstance": "Sharing this local instance will take a few moments. A success message and updated details will be displayed upon completion.",
"warning.holdingsRecord.suppressedFromDiscovery": "Warning: Holdings is marked suppressed from discovery",
"warning.item.suppressedFromDiscovery": "Warning: Item is marked suppressed from discovery",
"discoverySuppressed": "Suppressed from discovery",
From e5836008f6dfd32baab98fd7453f296bb8687986 Mon Sep 17 00:00:00 2001
From: Oleksandr Hladchenko
<85172747+OleksandrHladchenko1@users.noreply.github.com>
Date: Fri, 27 Oct 2023 14:57:07 +0300
Subject: [PATCH 2/2] UIIN-2452: Fix creating/editing/duplicating
holdings/items for different member tenants (follow-up) (#2319)
* 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
* UIIN-2452: Change permissions to view/create holdings and items
* UIIN-2452: Change permissions to view/create holdings and items
* UIIN-2452: Instance 3rd pane: Enable/disable consortial holdings/item actions based on User permissions (follow-up)
* UIIN-2452: Fixes in CreateHoldings component
* UIIN-2452: Fix test
* UIIN-2452: fix creating holdings/items for different member tenants
* UIIN-2452: Fix code smells
* UIIN-2452: Fix tests
* UIIN-2452: Remove excessive permission checking
---------
Co-authored-by: Mariia_Aloshyna
Co-authored-by: Mariia Aloshyna <55138456+mariia-aloshyna@users.noreply.github.com>
---
src/Holding/CreateHolding/CreateHolding.js | 11 ++---
.../DuplicateHolding/DuplicateHolding.js | 25 +++++++++--
src/Holding/EditHolding/EditHolding.js | 25 ++++++++---
.../Holding/HoldingButtonsGroup.js | 8 +++-
.../HoldingsList/HoldingsListContainer.js | 13 +++---
.../InstanceDetails/InstanceDetails.js | 2 +-
.../InstanceNewHolding/InstanceNewHolding.js | 35 +++++++---------
.../InstanceNewHolding.test.js | 2 +-
.../MemberTenantHoldings.js | 11 +++--
.../MemberTenantHoldings.test.js | 2 +-
src/Instance/ItemsList/ItemBarcode.js | 4 +-
src/Item/CreateItem/CreateItem.js | 10 ++---
src/Item/DuplicateItem/DuplicateItem.js | 19 +++++++--
src/Item/EditItem/EditItem.js | 13 ++++--
src/ViewHoldingsRecord.js | 36 +++++++++++++---
src/ViewHoldingsRecord.test.js | 35 +++++++++++-----
src/routes/ItemRoute.js | 25 -----------
src/utils.js | 2 +-
src/views/ItemView.js | 42 +++++++++++++++++--
19 files changed, 210 insertions(+), 110 deletions(-)
diff --git a/src/Holding/CreateHolding/CreateHolding.js b/src/Holding/CreateHolding/CreateHolding.js
index 1cfcceef9..d778690ee 100644
--- a/src/Holding/CreateHolding/CreateHolding.js
+++ b/src/Holding/CreateHolding/CreateHolding.js
@@ -30,7 +30,7 @@ const CreateHolding = ({
const callout = useCallout();
const { instance, isLoading: isInstanceLoading } = useInstance(instanceId);
const sourceId = referenceData.holdingsSourcesByName?.FOLIO?.id;
- const { location: { state: { tenantFrom } } } = history;
+ const tenantFrom = location?.state?.tenantFrom || stripes.okapi.tenant;
const goBack = useCallback(() => {
history.push({
@@ -39,13 +39,15 @@ const CreateHolding = ({
});
}, [location.search, instanceId]);
- const onCancel = useCallback(() => {
- switchAffiliation(stripes, tenantFrom, goBack);
+ const onCancel = useCallback(async () => {
+ await switchAffiliation(stripes, tenantFrom, goBack);
}, [stripes, tenantFrom, goBack]);
const onSubmit = useCallback((newHolding) => {
return mutator.holding.POST(newHolding)
- .then((holdingsRecord) => {
+ .then(async (holdingsRecord) => {
+ await onCancel();
+
callout.sendCallout({
type: 'success',
message: ,
});
- onCancel();
});
}, [onCancel, callout]);
diff --git a/src/Holding/DuplicateHolding/DuplicateHolding.js b/src/Holding/DuplicateHolding/DuplicateHolding.js
index 96fb49f32..eac3e2078 100644
--- a/src/Holding/DuplicateHolding/DuplicateHolding.js
+++ b/src/Holding/DuplicateHolding/DuplicateHolding.js
@@ -16,13 +16,20 @@ import {
} from '../../hooks';
import HoldingsForm from '../../edit/holdings/HoldingsForm';
import withLocation from '../../withLocation';
+import { switchAffiliation } from '../../utils';
const DuplicateHolding = ({
goTo,
history,
instanceId,
holdingId,
- location: { search, state: locationState },
+ location: {
+ search,
+ state: {
+ backPathname: locationState,
+ tenantFrom,
+ } = {},
+ },
referenceTables,
}) => {
const callout = useCallout();
@@ -37,10 +44,18 @@ const DuplicateHolding = ({
sourceId,
}), [holding, sourceId]);
+ const goToDuplicatedHolding = useCallback((id) => {
+ history.push({
+ pathname: `/inventory/view/${instanceId}/${id}`,
+ search,
+ state: { tenantTo: stripes.okapi.tenant },
+ });
+ }, [search, instanceId]);
+
const onSuccess = useCallback(async (response) => {
const { id, hrid } = await response.json();
- goTo(`/inventory/view/${instanceId}/${id}`);
+ await switchAffiliation(stripes, tenantFrom, () => goToDuplicatedHolding(id));
return callout.sendCallout({
type: 'success',
@@ -53,13 +68,17 @@ const DuplicateHolding = ({
const { mutateHolding } = useHoldingMutation({ onSuccess });
- const onCancel = useCallback(() => {
+ const goBack = useCallback(() => {
history.push({
pathname: locationState?.backPathname ?? `/inventory/view/${instanceId}`,
search,
});
}, [search, instanceId]);
+ const onCancel = useCallback(async () => {
+ await switchAffiliation(stripes, tenantFrom, goBack);
+ }, [stripes, tenantFrom, goBack]);
+
const onSubmit = useCallback(holdingValues => (
mutateHolding(holdingValues)
), [mutateHolding]);
diff --git a/src/Holding/EditHolding/EditHolding.js b/src/Holding/EditHolding/EditHolding.js
index bb9e3432e..4a855213f 100644
--- a/src/Holding/EditHolding/EditHolding.js
+++ b/src/Holding/EditHolding/EditHolding.js
@@ -17,7 +17,10 @@ import {
} from '../../hooks';
import HoldingsForm from '../../edit/holdings/HoldingsForm';
import withLocation from '../../withLocation';
-import { parseHttpError } from '../../utils';
+import {
+ parseHttpError,
+ switchAffiliation,
+} from '../../utils';
const EditHolding = ({
goTo,
@@ -28,7 +31,13 @@ const EditHolding = ({
referenceTables,
}) => {
const callout = useCallout();
- const { search, state: locationState } = location;
+ const {
+ search,
+ state: {
+ backPathname: locationState,
+ tenantFrom,
+ } = {},
+ } = location;
const stripes = useStripes();
const [httpError, setHttpError] = useState();
const { instance, isLoading: isInstanceLoading } = useInstanceQuery(instanceId);
@@ -42,17 +51,21 @@ const EditHolding = ({
referenceTables?.holdingsSources?.find(source => source.id === holding?.sourceId)?.name === 'MARC'
), [holding]);
- const onCancel = useCallback(() => {
+ const goBack = useCallback(() => {
history.push({
pathname: locationState?.backPathname ?? `/inventory/view/${instanceId}`,
search,
});
}, [search, instanceId]);
- const onSuccess = useCallback(() => {
- onCancel();
+ const onCancel = useCallback(async () => {
+ await switchAffiliation(stripes, tenantFrom, goBack);
+ }, [stripes, tenantFrom, goBack]);
+
+ const onSuccess = useCallback(async () => {
+ await onCancel();
- return callout.sendCallout({
+ callout.sendCallout({
type: 'success',
message: switchAffiliation(stripes, tenantId, onViewHolding)}
+ onClick={async () => {
+ await switchAffiliation(stripes, tenantId, onViewHolding);
+ }}
>
@@ -58,7 +60,9 @@ const HoldingButtonsGroup = ({