diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc14612d..b26a0a90 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,6 +3,8 @@
## 6.0.0 (IN PROGRESS)
* *BREAKING* Display all versions in change log in fourth pane. Refs UIORGS-355.
+* Show in version history record view, which fields have been edited. Refs UIORGS-356.
+* Adapt organization metadata fields to version history mechanism. Refs UIORGS-359.
## [5.2.0](https://github.com/folio-org/ui-organizations/tree/v5.2.0) (2024-10-31)
[Full Changelog](https://github.com/folio-org/ui-organizations/compare/v5.1.1...v5.2.0)
diff --git a/src/ContactPeople/ContactPerson/index.js b/src/ContactPeople/ContactPerson/index.js
index 01188197..1ea57a2f 100644
--- a/src/ContactPeople/ContactPerson/index.js
+++ b/src/ContactPeople/ContactPerson/index.js
@@ -1 +1,2 @@
export { default } from './ContactPerson';
+export { default as ContactPersonSection } from './ContactPersonSection';
diff --git a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.test.js b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.test.js
index bcc3786d..605b24bf 100644
--- a/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.test.js
+++ b/src/Organizations/OrganizationForm/OrganizationBankingInfoForm/OrganizationBankingInfoForm.test.js
@@ -92,7 +92,7 @@ describe('OrganizationBankingInfoForm', () => {
await user.click(screen.getAllByText('stripes-components.selection.controlLabel')[1]);
bankingAccountTypes.forEach(({ name }) => {
- expect(screen.getByText(name)).toBeInTheDocument();
+ expect(screen.getAllByText(name)[0]).toBeInTheDocument();
});
});
@@ -101,10 +101,10 @@ describe('OrganizationBankingInfoForm', () => {
renderOrganizationBankingInfoForm();
await addField();
- await user.click(screen.getAllByText('stripes-components.selection.controlLabel')[0]);
+ await user.click(screen.getByRole('button', { name: 'ui-organizations.data.bankingInformation.addressCategory' }));
categories.forEach(({ value }) => {
- expect(within(screen.getByTestId('banking-information-card')).getByText(value)).toBeInTheDocument();
+ expect(screen.getAllByText(value)[0]).toBeInTheDocument();
});
});
diff --git a/src/Organizations/OrganizationVersion/OrganizationVersion.js b/src/Organizations/OrganizationVersion/OrganizationVersion.js
index 2f52d21f..c6551cf6 100644
--- a/src/Organizations/OrganizationVersion/OrganizationVersion.js
+++ b/src/Organizations/OrganizationVersion/OrganizationVersion.js
@@ -21,7 +21,11 @@ import {
} from '../../common/constants';
import { HIDDEN_FIELDS_FOR_ORGANIZATION_VERSION_HISTORY } from '../constants';
import { getOrganizationFieldsLabelMap } from './getOrganizationFieldsLabelMap';
-import { useOrganizationVersions } from './hooks';
+import {
+ useOrganizationVersions,
+ useSelectedOrganizationVersion,
+} from './hooks';
+import { OrganizationVersionView } from './OrganizationVersionView';
const OrganizationVersion = ({
history,
@@ -62,8 +66,15 @@ const OrganizationVersion = ({
},
});
+ const {
+ isLoading: isOrganizationVersionLoading,
+ selectedVersion,
+ } = useSelectedOrganizationVersion({ versionId, versions, snapshotPath });
+
const isVersionLoading = (
- isOrganizationLoading || isHistoryLoading
+ isOrganizationLoading
+ || isHistoryLoading
+ || isOrganizationVersionLoading
);
const labelsMap = useMemo(() => getOrganizationFieldsLabelMap(), []);
@@ -84,7 +95,7 @@ const OrganizationVersion = ({
tags={get(organization, 'tags.tagList', [])}
versionId={versionId}
>
- {/* TODO: https://folio-org.atlassian.net/browse/UIORGS-356 */}
+
({
const { organizationSnapshot, ...auditEvent } = organizationAuditEvent;
const latestSnapshot = {
- ...organizationSnapshot,
+ ...organizationSnapshot.map,
edition: 'Second edition',
};
const originalSnapshot = { ...organizationSnapshot };
diff --git a/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.js b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.js
new file mode 100644
index 00000000..d96763da
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.js
@@ -0,0 +1,147 @@
+import PropTypes from 'prop-types';
+import { useRef } from 'react';
+
+import {
+ Accordion,
+ AccordionSet,
+ AccordionStatus,
+ checkScope,
+ Col,
+ collapseAllSections,
+ ExpandAllButton,
+ expandAllSections,
+ HasCommand,
+ Row,
+} from '@folio/stripes/components';
+
+import {
+ ORGANIZATION_SECTION_LABELS,
+ ORGANIZATION_SECTIONS,
+} from '../../constants';
+import {
+ OrganizationAccountsVersionView,
+ OrganizationContactInfoVersionView,
+ OrganizationContactPeopleVersionView,
+ OrganizationInterfacesVersionView,
+ OrganizationSummaryVersionView,
+ OrganizationVendorInfoVersionView,
+ OrganizationVendorTermsVersionView,
+} from '../components';
+
+const initialAccordionStatus = {
+ [ORGANIZATION_SECTIONS.summarySection]: true,
+ [ORGANIZATION_SECTIONS.contactInformationSection]: false,
+ [ORGANIZATION_SECTIONS.contactPeopleSection]: true,
+ [ORGANIZATION_SECTIONS.interfacesSection]: false,
+ [ORGANIZATION_SECTIONS.vendorInformationSection]: false,
+ [ORGANIZATION_SECTIONS.vendorTermsSection]: false,
+ [ORGANIZATION_SECTIONS.integrationDetailsSection]: false,
+ [ORGANIZATION_SECTIONS.accountsSection]: false,
+ [ORGANIZATION_SECTIONS.agreements]: false,
+};
+
+export const OrganizationVersionView = ({ version }) => {
+ const accordionStatusRef = useRef();
+
+ const shortcuts = [
+ {
+ name: 'expandAllSections',
+ handler: (e) => expandAllSections(e, accordionStatusRef),
+ },
+ {
+ name: 'collapseAllSections',
+ handler: (e) => collapseAllSections(e, accordionStatusRef),
+ },
+ ];
+
+ return (
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ {
+ version?.isVendor && (
+ <>
+
+
+
+
+
+
+
+
+
+
+
+ >
+ )
+ }
+
+
+
+ );
+};
+
+OrganizationVersionView.propTypes = {
+ version: PropTypes.object,
+};
diff --git a/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js
new file mode 100644
index 00000000..63b2450f
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js
@@ -0,0 +1,68 @@
+import {
+ QueryClient,
+ QueryClientProvider,
+} from 'react-query';
+import { MemoryRouter } from 'react-router-dom';
+
+import {
+ render,
+ screen,
+} from '@folio/jest-config-stripes/testing-library/react';
+import { useCategories } from '@folio/stripes-acq-components';
+
+import {
+ organization,
+ organizationAuditEvent,
+} from 'fixtures';
+import { ORGANIZATION_VERSIONS_VIEW_ROUTE } from '../../../common/constants';
+import { OrganizationVersionView } from './OrganizationVersionView';
+
+jest.mock('@folio/stripes-acq-components', () => ({
+ ...jest.requireActual('@folio/stripes-acq-components'),
+ useCategories: jest.fn(),
+}));
+
+const { organizationSnapshot } = organizationAuditEvent;
+
+const queryClient = new QueryClient();
+const wrapper = ({ children }) => (
+
+
+ {children}
+
+
+);
+
+const renderOrganizationVersionView = (props = {}) => render(
+ ,
+ { wrapper },
+);
+
+describe('OrganizationVersion', () => {
+ beforeEach(() => {
+ useCategories.mockReturnValue({ categories: [{ id: 'f52ceea4-8e35-404b-9ebd-5c7db6613195', value: 'cat' }] });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should render version history view', async () => {
+ renderOrganizationVersionView();
+
+ expect(screen.getByText('ui-organizations.summary')).toBeInTheDocument();
+ expect(screen.getByText('ui-organizations.contactInformation')).toBeInTheDocument();
+ expect(screen.getByText('ui-organizations.contactPeople')).toBeInTheDocument();
+ expect(screen.getByText('ui-organizations.interface')).toBeInTheDocument();
+ expect(screen.getByText('ui-organizations.vendorInformation')).toBeInTheDocument();
+ expect(screen.getByText('ui-organizations.vendorTerms')).toBeInTheDocument();
+ expect(screen.getByText('ui-organizations.accounts')).toBeInTheDocument();
+ });
+});
diff --git a/src/Organizations/OrganizationVersion/OrganizationVersionView/index.js b/src/Organizations/OrganizationVersion/OrganizationVersionView/index.js
new file mode 100644
index 00000000..90186b5f
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/OrganizationVersionView/index.js
@@ -0,0 +1 @@
+export { OrganizationVersionView } from './OrganizationVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js
new file mode 100644
index 00000000..b09ebc50
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js
@@ -0,0 +1,122 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Col,
+ Row,
+} from '@folio/stripes/components';
+import {
+ PAYMENT_METHOD_LABELS,
+ VersionKeyValue,
+} from '@folio/stripes-acq-components';
+
+import css from '../../../OrganizationDetails/OrganizationAccounts/OrganizationAccount/OrganizationAccount.css';
+
+export const OrganizationAccountsVersionView = ({ name, version }) => {
+ if (!version?.accounts?.length) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+ <>
+ {version?.accounts?.map((account, indx) => {
+ return (
+
+
+ }
+ value={account?.name}
+ />
+
+
+
+ }
+ value={account?.accountNo}
+ />
+
+
+
+ }
+ value={account?.description}
+ />
+
+
+
+ }
+ value={account?.appSystemNo}
+ />
+
+
+
+ }
+ value={PAYMENT_METHOD_LABELS[account?.paymentMethod]}
+ />
+
+
+
+ }
+ value={account?.contactInfo}
+ />
+
+
+
+ }
+ value={account?.libraryCode}
+ />
+
+
+
+ }
+ value={account?.libraryEdiCode}
+ />
+
+
+
+ }
+ value={account?.notes}
+ />
+
+
+
+ }
+ value={account?.acqUnits}
+ multiple
+ />
+
+
+ );
+ })}
+ >
+ );
+};
+
+OrganizationAccountsVersionView.propTypes = {
+ name: PropTypes.string.isRequired,
+ version: PropTypes.object,
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/index.js b/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/index.js
new file mode 100644
index 00000000..8b3dfc55
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/index.js
@@ -0,0 +1 @@
+export { OrganizationAccountsVersionView } from './OrganizationAccountsVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/OrganizationContactInfoVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/OrganizationContactInfoVersionView.js
new file mode 100644
index 00000000..1e8f8c0f
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/OrganizationContactInfoVersionView.js
@@ -0,0 +1,98 @@
+import cloneDeep from 'lodash/cloneDeep';
+import get from 'lodash/get';
+import PropTypes from 'prop-types';
+import { useMemo } from 'react';
+
+import {
+ Accordion,
+ Layout,
+} from '@folio/stripes/components';
+import { useCategories } from '@folio/stripes-acq-components';
+
+import {
+ UNCATEGORIZED_ID,
+ UNCATEGORIZED_VALUE,
+} from '../../../../common/constants';
+import {
+ hydrateAddresses,
+ mixCategories,
+} from '../../../../common/utils';
+import {
+ ContactAddressesVersionView,
+ ContactPersonEmailsVersionView,
+ ContactPersonPhonesVersionView,
+ ContactPersonURLsVersionView,
+} from './components';
+
+const setInitialArrayFieldPaths = (obj, paths) => {
+ return paths.reduce((result, path) => {
+ const target = get(result, path, []);
+
+ target.forEach((item, indx) => {
+ item._initialFieldPath = `${path}[${indx}]`;
+ });
+
+ return result;
+ }, cloneDeep(obj ?? {}));
+};
+
+export const OrganizationContactInfoVersionView = ({ version: currentVersion }) => {
+ /*
+ Entities are grouped by categories, so the order in the arrays is not guaranteed.
+ So we need to keep the initial field paths
+ */
+ const version = setInitialArrayFieldPaths(currentVersion, ['addresses', 'emails', 'phoneNumbers', 'urls']);
+
+ const { categories } = useCategories();
+
+ const groups = useMemo(() => [
+ ...(categories ?? []),
+ {
+ id: UNCATEGORIZED_ID,
+ value: UNCATEGORIZED_VALUE,
+ },
+ ], [categories]);
+
+ const data = useMemo(() => groups.reduce((acc, { id }) => {
+ const filterCb = ({ categories: entityCategories = [] }) => {
+ return id === UNCATEGORIZED_ID ? entityCategories.length === 0 : entityCategories.includes(id);
+ };
+
+ const addresses = (version?.addresses || []).filter(filterCb);
+ const emails = (version?.emails || []).filter(filterCb);
+ const phoneNumbers = (version?.phoneNumbers || []).filter(filterCb);
+ const urls = (version?.urls || []).filter(filterCb);
+
+ if (addresses.length || emails.length || phoneNumbers.length || urls.length) {
+ acc[id] = {
+ addresses: hydrateAddresses(categories, addresses),
+ emails: mixCategories(categories, emails),
+ phoneNumbers: mixCategories(categories, phoneNumbers),
+ urls: mixCategories(categories, urls),
+ };
+ }
+
+ return acc;
+ }, {}), [categories, groups, version]);
+
+ return (
+
+ {groups.map((category) => data[category.id] && (
+
+
+
+
+
+
+ ))}
+
+ );
+};
+
+OrganizationContactInfoVersionView.propTypes = {
+ version: PropTypes.object,
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js
new file mode 100644
index 00000000..aac1908b
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js
@@ -0,0 +1,97 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Card,
+ Col,
+ Row,
+} from '@folio/stripes/components';
+import { VersionKeyValue } from '@folio/stripes-acq-components';
+
+import { ContactCardHeaderVersionView } from './ContactCardHeaderVersionView';
+
+export const ContactAddressesVersionView = ({ addresses }) => {
+ return (
+
+
+ {addresses?.map((address) => {
+ return (
+
+ )}
+ >
+
+
+ }
+ value={address?.addressLine1}
+ />
+
+
+ }
+ value={address?.addressLine2}
+ />
+
+
+ }
+ value={address?.city}
+ />
+
+
+ }
+ value={address?.stateRegion}
+ />
+
+
+ }
+ value={address?.zipCode}
+ />
+
+
+ }
+ value={address?.country}
+ />
+
+
+ }
+ value={address?.language}
+ />
+
+
+ }
+ value={address?.categories}
+ multiple
+ />
+
+
+
+ );
+ })}
+
+
+ );
+};
+
+ContactAddressesVersionView.propTypes = {
+ addresses: PropTypes.arrayOf(PropTypes.object),
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.css b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.css
new file mode 100644
index 00000000..ac0e1af8
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.css
@@ -0,0 +1,7 @@
+.cardHeader {
+ margin: 0;
+
+ & .mark {
+ background-color: mark;
+ }
+}
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.js
new file mode 100644
index 00000000..ac903107
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.js
@@ -0,0 +1,28 @@
+import PropTypes from 'prop-types';
+import { useContext } from 'react';
+import { FormattedMessage } from 'react-intl';
+
+import { VersionViewContext } from '@folio/stripes-acq-components';
+
+import css from './ContactCardHeaderVersionView.css';
+
+export const ContactCardHeaderVersionView = ({ isPrimary, name }) => {
+ const versionContext = useContext(VersionViewContext);
+
+ const isUpdated = versionContext?.paths?.includes(name);
+
+ const headerStart = (
+
+
+
+
+
+ );
+
+ return headerStart;
+};
+
+ContactCardHeaderVersionView.propTypes = {
+ isPrimary: PropTypes.bool.isRequired,
+ name: PropTypes.string.isRequired,
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/index.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/index.js
new file mode 100644
index 00000000..f958e7ee
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/index.js
@@ -0,0 +1 @@
+export { ContactCardHeaderVersionView } from './ContactCardHeaderVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonEmailsVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonEmailsVersionView.js
new file mode 100644
index 00000000..80d17f0e
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonEmailsVersionView.js
@@ -0,0 +1,82 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Card,
+ Col,
+ Row,
+} from '@folio/stripes/components';
+import {
+ LANG_LABEL_BY_CODE,
+ VersionKeyValue,
+} from '@folio/stripes-acq-components';
+
+import { ContactPersonSection } from '../../../../../ContactPeople/ContactPerson';
+import { ContactCardHeaderVersionView } from './ContactCardHeaderVersionView';
+
+export const ContactPersonEmailsVersionView = ({ emails }) => {
+ if (!emails?.length) return null;
+
+ const renderBody = () => (
+
+
+ {emails?.map((email) => {
+ return (
+
+ )}
+ >
+
+
+ }
+ value={email?.value}
+ />
+
+
+ }
+ value={email?.description}
+ />
+
+
+ }
+ value={LANG_LABEL_BY_CODE[email?.language] || email?.language}
+ />
+
+
+ }
+ value={email.categories}
+ multiple
+ />
+
+
+
+ );
+ })}
+
+
+ );
+
+ return (
+ }
+ renderBody={renderBody}
+ />
+ );
+};
+
+ContactPersonEmailsVersionView.propTypes = {
+ emails: PropTypes.arrayOf(PropTypes.object),
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js
new file mode 100644
index 00000000..672d1333
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js
@@ -0,0 +1,82 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Card,
+ Col,
+ Row,
+} from '@folio/stripes/components';
+import {
+ LANG_LABEL_BY_CODE,
+ VersionKeyValue,
+} from '@folio/stripes-acq-components';
+
+import { ContactPersonSection } from '../../../../../ContactPeople/ContactPerson';
+import { ContactCardHeaderVersionView } from './ContactCardHeaderVersionView';
+
+export const ContactPersonPhonesVersionView = ({ phones }) => {
+ if (!phones?.length) return null;
+
+ const renderBody = () => (
+
+
+ {phones?.map((phone) => {
+ return (
+
+ )}
+ >
+
+
+ }
+ value={phone?.phoneNumber}
+ />
+
+
+ }
+ value={phone?.type}
+ />
+
+
+ }
+ value={LANG_LABEL_BY_CODE[phone?.language] || phone?.language}
+ />
+
+
+ }
+ value={phone.categories}
+ multiple
+ />
+
+
+
+ );
+ })}
+
+
+ );
+
+ return (
+ }
+ renderBody={renderBody}
+ />
+ );
+};
+
+ContactPersonPhonesVersionView.propTypes = {
+ phones: PropTypes.arrayOf(PropTypes.object),
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonURLsVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonURLsVersionView.js
new file mode 100644
index 00000000..e4c4e8a7
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonURLsVersionView.js
@@ -0,0 +1,90 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Card,
+ Col,
+ Row,
+ TextLink,
+} from '@folio/stripes/components';
+import {
+ LANG_LABEL_BY_CODE,
+ VersionKeyValue,
+} from '@folio/stripes-acq-components';
+
+import { ContactPersonSection } from '../../../../../ContactPeople/ContactPerson';
+import { ContactCardHeaderVersionView } from './ContactCardHeaderVersionView';
+
+export const ContactPersonURLsVersionView = ({ urls }) => {
+ if (!urls?.length) return null;
+
+ const renderBody = () => (
+
+
+ {urls?.map((url) => {
+ return (
+
+ )}
+ >
+
+
+ }
+ >
+
+ {url.value}
+
+
+
+
+ }
+ value={url?.description}
+ />
+
+
+ }
+ value={LANG_LABEL_BY_CODE[url?.language] || url?.language}
+ />
+
+
+ }
+ value={url.categories}
+ multiple
+ />
+
+
+
+ );
+ })}
+
+
+ );
+
+ return (
+ }
+ renderBody={renderBody}
+ />
+ );
+};
+
+ContactPersonURLsVersionView.propTypes = {
+ urls: PropTypes.arrayOf(PropTypes.object),
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/index.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/index.js
new file mode 100644
index 00000000..6810bca3
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/index.js
@@ -0,0 +1,4 @@
+export { ContactAddressesVersionView } from './ContactAddressesVersionView';
+export { ContactPersonEmailsVersionView } from './ContactPersonEmailsVersionView';
+export { ContactPersonPhonesVersionView } from './ContactPersonPhonesVersionView';
+export { ContactPersonURLsVersionView } from './ContactPersonURLsVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/index.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/index.js
new file mode 100644
index 00000000..bc291929
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/index.js
@@ -0,0 +1 @@
+export { OrganizationContactInfoVersionView } from './OrganizationContactInfoVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js
new file mode 100644
index 00000000..2e40d058
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js
@@ -0,0 +1,79 @@
+import find from 'lodash/find';
+import get from 'lodash/get';
+import PropTypes from 'prop-types';
+import { useMemo } from 'react';
+import {
+ FormattedMessage,
+ useIntl,
+} from 'react-intl';
+
+import {
+ MultiColumnList,
+ NoValue,
+} from '@folio/stripes/components';
+import { useCategories } from '@folio/stripes-acq-components';
+
+import { Ellipsis } from '../../../../common/components';
+import { useVersionWrappedRowFormatter } from '../../../../common/hooks';
+import { transformCategoryIdsToLables } from '../../../../common/utils';
+
+const visibleColumns = [
+ 'name',
+ 'categories',
+ 'email',
+ 'phone',
+ 'status',
+ 'notes',
+];
+
+const columnMapping = {
+ name: ,
+ categories: ,
+ email: ,
+ phone: ,
+ status: ,
+ notes: ,
+};
+
+const getResultsFormatter = ({ intl, categories }) => ({
+ name: ({ isDeleted, firstName, lastName }) => (
+ isDeleted
+ ? intl.formatMessage({ id: 'ui-organizations.contactPeople.removedContact' })
+ : `${lastName}, ${firstName}`
+ ),
+ categories: ({ categories: vendorCategories = [] }) => {
+ return transformCategoryIdsToLables(categories, vendorCategories) || ;
+ },
+ email: c => get(find(c.emails, 'isPrimary'), 'value', '') || ,
+ phone: c => get(find(c.phoneNumbers, 'isPrimary'), 'phoneNumber', '') || ,
+ status: c => ,
+ notes: c => {c.notes},
+});
+
+export const OrganizationContactPeopleVersionView = ({ name, version }) => {
+ const intl = useIntl();
+
+ const { categories } = useCategories();
+
+ const rowFormatter = useVersionWrappedRowFormatter({ name });
+
+ const resultsFormatter = useMemo(() => {
+ return getResultsFormatter({ intl, categories });
+ }, [intl, categories]);
+
+ return (
+
+ );
+};
+
+OrganizationContactPeopleVersionView.propTypes = {
+ name: PropTypes.string.isRequired,
+ version: PropTypes.object,
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/index.js b/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/index.js
new file mode 100644
index 00000000..72ff74a1
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/index.js
@@ -0,0 +1 @@
+export { OrganizationContactPeopleVersionView } from './OrganizationContactPeopleVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationInterfacesVersionView/OrganizationInterfacesVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationInterfacesVersionView/OrganizationInterfacesVersionView.js
new file mode 100644
index 00000000..b7a2570a
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationInterfacesVersionView/OrganizationInterfacesVersionView.js
@@ -0,0 +1,63 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Col,
+ MultiColumnList,
+ NoValue,
+ Row,
+ TextLink,
+} from '@folio/stripes/components';
+
+import { useVersionWrappedRowFormatter } from '../../../../common/hooks';
+
+const columnMapping = {
+ interfaceName: ,
+ interfaceUrl: ,
+};
+
+const visibleColumns = ['interfaceName', 'interfaceUrl'];
+
+const resultsFormatter = {
+ interfaceName: ({ name }) => name,
+ interfaceUrl: (item) => (
+ item.uri
+ ? (
+
+ {item.uri}
+
+ )
+ :
+ ),
+};
+
+export const OrganizationInterfacesVersionView = ({
+ name,
+ version,
+}) => {
+ const rowFormatter = useVersionWrappedRowFormatter({ name });
+
+ return (
+
+
+
+
+
+ );
+};
+
+OrganizationInterfacesVersionView.propTypes = {
+ name: PropTypes.string.isRequired,
+ version: PropTypes.object,
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationInterfacesVersionView/index.js b/src/Organizations/OrganizationVersion/components/OrganizationInterfacesVersionView/index.js
new file mode 100644
index 00000000..06da8449
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationInterfacesVersionView/index.js
@@ -0,0 +1 @@
+export { OrganizationInterfacesVersionView } from './OrganizationInterfacesVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationSummaryVersionView/OrganizationSummaryVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationSummaryVersionView/OrganizationSummaryVersionView.js
new file mode 100644
index 00000000..2385fd1a
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationSummaryVersionView/OrganizationSummaryVersionView.js
@@ -0,0 +1,130 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Col,
+ NoValue,
+ Row,
+} from '@folio/stripes/components';
+import { ViewMetaData } from '@folio/stripes/smart-components';
+import {
+ LANG_LABEL_BY_CODE,
+ VersionCheckbox,
+ VersionKeyValue,
+} from '@folio/stripes-acq-components';
+
+import { ORGANIZATION_SECTIONS } from '../../../constants';
+
+export const OrganizationSummaryVersionView = ({ version }) => {
+ return (
+ <>
+
+
+ {version?.metadata && (
+
+ )}
+
+
+
+
+
+ }
+ value={version?.name}
+ />
+
+
+
+ }
+ value={version?.code}
+ />
+
+
+
+ }
+ value={version?.erpCode || }
+ />
+
+
+
+ }
+ value={version?.status && }
+ />
+
+
+
+ }
+ value={LANG_LABEL_BY_CODE[version?.language] || version?.language || }
+ />
+
+
+
+ }
+ value={version?.organizationTypes?.join(', ') || }
+ multiple
+ />
+
+
+
+ }
+ value={version?.acqUnits}
+ multiple
+ />
+
+
+
+ }
+ value={version?.description || }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ />
+
+
+
+ }
+ value={version?.alternativeNames || }
+ multiple
+ />
+
+
+ >
+ );
+};
+
+OrganizationSummaryVersionView.propTypes = {
+ version: PropTypes.object,
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationSummaryVersionView/index.js b/src/Organizations/OrganizationVersion/components/OrganizationSummaryVersionView/index.js
new file mode 100644
index 00000000..bb3ea69e
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationSummaryVersionView/index.js
@@ -0,0 +1 @@
+export { OrganizationSummaryVersionView } from './OrganizationSummaryVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationVendorInfoVersionView/OrganizationVendorInfoVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationVendorInfoVersionView/OrganizationVendorInfoVersionView.js
new file mode 100644
index 00000000..dfe40ec7
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationVendorInfoVersionView/OrganizationVendorInfoVersionView.js
@@ -0,0 +1,135 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ PAYMENT_METHOD_LABELS,
+ VersionCheckbox,
+ VersionKeyValue,
+} from '@folio/stripes-acq-components';
+import {
+ Col,
+ Row,
+} from '@folio/stripes/components';
+
+export const OrganizationVendorInfoVersionView = ({ version }) => {
+ return (
+
+
+ }
+ value={PAYMENT_METHOD_LABELS[version?.paymentMethod]}
+ />
+
+
+
+ }
+ value={version?.vendorCurrenciesValue}
+ multiple
+ />
+
+
+
+
+
+
+
+ }
+ value={version?.claimingInterval}
+ />
+
+
+
+ }
+ value={version?.discountPercent}
+ />
+
+
+
+ }
+ value={version?.expectedActivationInterval}
+ />
+
+
+
+ }
+ value={version?.expectedInvoiceInterval}
+ />
+
+
+
+ }
+ value={version?.expectedReceiptInterval}
+ />
+
+
+
+ }
+ value={version?.renewalActivationInterval}
+ />
+
+
+
+ }
+ value={version?.subscriptionInterval}
+ />
+
+
+
+ }
+ />
+
+
+
+
+
+
+
+ }
+ value={version?.taxId}
+ />
+
+
+
+ }
+ value={version?.taxPercentage}
+ />
+
+
+
+ }
+ />
+
+
+ );
+};
+
+OrganizationVendorInfoVersionView.propTypes = {
+ version: PropTypes.object,
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationVendorInfoVersionView/index.js b/src/Organizations/OrganizationVersion/components/OrganizationVendorInfoVersionView/index.js
new file mode 100644
index 00000000..745e857b
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationVendorInfoVersionView/index.js
@@ -0,0 +1 @@
+export { OrganizationVendorInfoVersionView } from './OrganizationVendorInfoVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationVendorTermsVersionView/OrganizationVendorTermsVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationVendorTermsVersionView/OrganizationVendorTermsVersionView.js
new file mode 100644
index 00000000..87eb069c
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationVendorTermsVersionView/OrganizationVendorTermsVersionView.js
@@ -0,0 +1,63 @@
+import PropTypes from 'prop-types';
+import { FormattedMessage } from 'react-intl';
+
+import {
+ Col,
+ NoValue,
+ Row,
+} from '@folio/stripes/components';
+import { VersionKeyValue } from '@folio/stripes-acq-components';
+
+export const OrganizationVendorTermsVersionView = ({ name, version }) => {
+ if (!version?.agreements?.length) {
+ return (
+
+
+
+ );
+ }
+
+ return (
+ <>
+ {version?.agreements?.map((agreement, indx) => {
+ return (
+
+
+ }
+ value={agreement?.name}
+ />
+
+
+ }
+ value={agreement?.discount !== undefined ? `${agreement?.discount}%` : }
+ />
+
+
+ }
+ value={agreement?.referenceUrl}
+ />
+
+
+ }
+ value={agreement?.notes}
+ />
+
+
+ );
+ })}
+ >
+ );
+};
+
+OrganizationVendorTermsVersionView.propTypes = {
+ name: PropTypes.string.isRequired,
+ version: PropTypes.object,
+};
diff --git a/src/Organizations/OrganizationVersion/components/OrganizationVendorTermsVersionView/index.js b/src/Organizations/OrganizationVersion/components/OrganizationVendorTermsVersionView/index.js
new file mode 100644
index 00000000..057611e4
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/OrganizationVendorTermsVersionView/index.js
@@ -0,0 +1 @@
+export { OrganizationVendorTermsVersionView } from './OrganizationVendorTermsVersionView';
diff --git a/src/Organizations/OrganizationVersion/components/index.js b/src/Organizations/OrganizationVersion/components/index.js
new file mode 100644
index 00000000..de2d6fa9
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/components/index.js
@@ -0,0 +1,7 @@
+export { OrganizationAccountsVersionView } from './OrganizationAccountsVersionView';
+export { OrganizationContactInfoVersionView } from './OrganizationContactInfoVersionView';
+export { OrganizationContactPeopleVersionView } from './OrganizationContactPeopleVersionView';
+export { OrganizationInterfacesVersionView } from './OrganizationInterfacesVersionView';
+export { OrganizationSummaryVersionView } from './OrganizationSummaryVersionView';
+export { OrganizationVendorInfoVersionView } from './OrganizationVendorInfoVersionView';
+export { OrganizationVendorTermsVersionView } from './OrganizationVendorTermsVersionView';
diff --git a/src/Organizations/OrganizationVersion/getOrganizationFieldsLabelMap.js b/src/Organizations/OrganizationVersion/getOrganizationFieldsLabelMap.js
index f2073cca..9da5b70f 100644
--- a/src/Organizations/OrganizationVersion/getOrganizationFieldsLabelMap.js
+++ b/src/Organizations/OrganizationVersion/getOrganizationFieldsLabelMap.js
@@ -30,44 +30,44 @@ export const getOrganizationFieldsLabelMap = () => {
'addresses[\\d].stateRegion': 'ui-organizations.contactPeople.stateRegion',
'addresses[\\d].zipCode': 'ui-organizations.contactPeople.zipCode',
'addresses[\\d].country': 'ui-organizations.contactPeople.country',
- 'addresses[\\d].isPrimary': 'ui-organizations.primaryItem',
- 'addresses[\\d].categories': 'ui-organizations.contactPeople.categories',
- 'addresses[\\d].categories[\\d]': 'ui-organizations.contactPeople.categories',
- 'addresses[\\d].language': 'ui-organizations.contactPeople.language',
+ 'addresses[\\d].isPrimary': 'ui-organizations.versionHistory.field.address.isPrimary',
+ 'addresses[\\d].categories': 'ui-organizations.versionHistory.field.address.categories',
+ 'addresses[\\d].categories[\\d]': 'ui-organizations.versionHistory.field.address.categories',
+ 'addresses[\\d].language': 'ui-organizations.versionHistory.field.address.language',
'phoneNumbers': 'ui-organizations.contactPeople.phoneNumbers',
'phoneNumbers[\\d]': 'ui-organizations.contactPeople.phoneNumbers',
'phoneNumbers[\\d].phoneNumber': 'ui-organizations.contactPeople.phoneNumber',
- 'phoneNumbers[\\d].categories': 'ui-organizations.contactPeople.categories',
- 'phoneNumbers[\\d].categories[\\d]': 'ui-organizations.contactPeople.categories',
- 'phoneNumbers[\\d].type': 'ui-organizations.contactPeople.type',
- 'phoneNumbers[\\d].isPrimary': 'ui-organizations.primaryItem',
+ 'phoneNumbers[\\d].categories': 'ui-organizations.versionHistory.field.phoneNumbers.categories',
+ 'phoneNumbers[\\d].categories[\\d]': 'ui-organizations.versionHistory.field.phoneNumbers.categories',
+ 'phoneNumbers[\\d].type': 'ui-organizations.versionHistory.field.phoneNumbers.type',
+ 'phoneNumbers[\\d].isPrimary': 'ui-organizations.versionHistory.field.phoneNumbers.isPrimary',
'phoneNumbers[\\d].language': 'ui-organizations.contactPeople.language',
'emails': 'ui-organizations.contactPeople.emails',
'emails[\\d]': 'ui-organizations.contactPeople.emails',
'emails[\\d].value': 'ui-organizations.contactPeople.emailAddress',
- 'emails[\\d].description': 'ui-organizations.contactPeople.description',
- 'emails[\\d].isPrimary': 'ui-organizations.primaryItem',
- 'emails[\\d].categories': 'ui-organizations.contactPeople.categories',
- 'emails[\\d].categories[\\d]': 'ui-organizations.contactPeople.categories',
- 'emails[\\d].language': 'ui-organizations.contactPeople.language',
+ 'emails[\\d].description': 'ui-organizations.versionHistory.field.emails.description',
+ 'emails[\\d].isPrimary': 'ui-organizations.versionHistory.field.emails.isPrimary',
+ 'emails[\\d].categories': 'ui-organizations.versionHistory.field.emails.categories',
+ 'emails[\\d].categories[\\d]': 'ui-organizations.versionHistory.field.emails.categories',
+ 'emails[\\d].language': 'ui-organizations.versionHistory.field.emails.language',
'urls': 'ui-organizations.contactPeople.urls',
'urls[\\d]': 'ui-organizations.contactPeople.urls',
'urls[\\d].value': 'ui-organizations.contactPeople.url',
- 'urls[\\d].description': 'ui-organizations.contactPeople.description',
- 'urls[\\d].language': 'ui-organizations.contactPeople.language',
- 'urls[\\d].isPrimary': 'ui-organizations.primaryItem',
- 'urls[\\d].categories': 'ui-organizations.contactPeople.categories',
- 'urls[\\d].categories[\\d]': 'ui-organizations.contactPeople.categories',
+ 'urls[\\d].description': 'ui-organizations.versionHistory.field.urls.description',
+ 'urls[\\d].language': 'ui-organizations.versionHistory.field.urls.language',
+ 'urls[\\d].isPrimary': 'ui-organizations.versionHistory.field.urls.isPrimary',
+ 'urls[\\d].categories': 'ui-organizations.versionHistory.field.urls.categories',
+ 'urls[\\d].categories[\\d]': 'ui-organizations.versionHistory.field.urls.categories',
'contacts': 'ui-organizations.contactPeople',
'contacts[\\d]': 'ui-organizations.contactPeople',
'privilegedContacts': 'ui-organizations.privilegedDonorInformation',
'agreements': 'ui-organizations.linkedAgreements.section',
'agreements[\\d]': 'ui-organizations.linkedAgreements.section',
- 'agreements[\\d].name': 'ui-organizations.agreement.name',
+ 'agreements[\\d].name': 'ui-organizations.versionHistory.field.agreements.name',
'agreements[\\d].discount': 'ui-organizations.agreement.discount',
'agreements[\\d].referenceUrl': 'ui-organizations.agreement.referenceUrl',
- 'agreements[\\d].notes': 'ui-organizations.agreement.notes',
+ 'agreements[\\d].notes': 'ui-organizations.versionHistory.field.agreements.notes',
'erpCode': 'ui-organizations.summary.accountingCode',
'paymentMethod': 'ui-organizations.accounts.paymentMethod',
'accessProvider': 'ui-organizations.versionHistory.field.accessProvider',
@@ -100,7 +100,7 @@ export const getOrganizationFieldsLabelMap = () => {
'edi.sendAcctNum': 'integration.edi.sendAccountNumber',
'edi.supportOrder': 'ui-organizations.integration.edi.orders',
'edi.supportInvoice': 'ui-organizations.integration.edi.invoices',
- 'edi.notes': 'ui-organizations.integration.edi.notes',
+ 'edi.notes': 'ui-organizations.versionHistory.field.edi.notes',
'edi.ediFtp': 'ui-organizations.integration.ftp',
'edi.ediFtp.ftpFormat': 'ui-organizations.integration.ftp.ftpFormat',
'edi.ediFtp.serverAddress': 'ui-organizations.integration.ftp.serverAddress',
@@ -130,18 +130,19 @@ export const getOrganizationFieldsLabelMap = () => {
'edi.ediJob.schedulingNotes': 'ui-organizations.versionHistory.field.edi.ediJob.schedulingNotes',
'accounts': 'ui-organizations.accounts',
- 'accounts.name': 'ui-organizations.accounts.name',
- 'accounts.accountNo': 'ui-organizations.accounts.accountNumber',
- 'accounts.description': 'ui-organizations.accounts.description',
- 'accounts.appSystemNo': 'ui-organizations.accounts.payable',
- 'accounts.paymentMethod': 'ui-organizations.accounts.paymentMethod',
- 'accounts.accountStatus': 'ui-organizations.accounts.account.accountStatus',
- 'accounts.contactInfo': 'ui-organizations.accounts.account.contactInfo',
- 'accounts.libraryCode': 'ui-organizations.accounts.libraryCode',
- 'accounts.libraryEdiCode': 'ui-organizations.accounts.libraryEDICode',
- 'accounts.notes': 'ui-organizations.accounts.notes',
- 'accounts.acqUnitIds': 'ui-organizations.versionHistory.field.accounts.acqUnitsIds',
- 'accounts.acqUnitIds[\\d]': 'ui-organizations.versionHistory.field.accounts.acqUnitsIds',
+ 'accounts[\\d]': 'ui-organizations.accounts',
+ 'accounts[\\d].name': 'ui-organizations.accounts.name',
+ 'accounts[\\d].accountNo': 'ui-organizations.accounts.accountNumber',
+ 'accounts[\\d].description': 'ui-organizations.accounts.description',
+ 'accounts[\\d].appSystemNo': 'ui-organizations.accounts.payable',
+ 'accounts[\\d].paymentMethod': 'ui-organizations.accounts.paymentMethod',
+ 'accounts[\\d].accountStatus': 'ui-organizations.accounts.account.accountStatus',
+ 'accounts[\\d].contactInfo': 'ui-organizations.accounts.account.contactInfo',
+ 'accounts[\\d].libraryCode': 'ui-organizations.accounts.libraryCode',
+ 'accounts[\\d].libraryEdiCode': 'ui-organizations.accounts.libraryEDICode',
+ 'accounts[\\d].notes': 'ui-organizations.accounts.notes',
+ 'accounts[\\d].acqUnitIds': 'ui-organizations.versionHistory.field.accounts.acqUnitsIds',
+ 'accounts[\\d].acqUnitIds[\\d]': 'ui-organizations.versionHistory.field.accounts.acqUnitsIds',
'isVendor': 'ui-organizations.summary.isVendor',
'isDonor': 'ui-organizations.summary.isDonor',
diff --git a/src/Organizations/OrganizationVersion/hooks/index.js b/src/Organizations/OrganizationVersion/hooks/index.js
index ecff7cfd..98d80689 100644
--- a/src/Organizations/OrganizationVersion/hooks/index.js
+++ b/src/Organizations/OrganizationVersion/hooks/index.js
@@ -1 +1,2 @@
export { useOrganizationVersions } from './useOrganizationVersions';
+export { useSelectedOrganizationVersion } from './useSelectedOrganizationVersion';
diff --git a/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/index.js b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/index.js
new file mode 100644
index 00000000..ebb54868
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/index.js
@@ -0,0 +1 @@
+export { useSelectedOrganizationVersion } from './useSelectedOrganizationVersion';
diff --git a/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js
new file mode 100644
index 00000000..be89fbf3
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js
@@ -0,0 +1,143 @@
+import filter from 'lodash/fp/filter';
+import flatMap from 'lodash/fp/flatMap';
+import flow from 'lodash/fp/flow';
+import get from 'lodash/fp/get';
+import keyBy from 'lodash/fp/keyBy';
+import uniq from 'lodash/fp/uniq';
+import { useMemo } from 'react';
+import { useIntl } from 'react-intl';
+import { useQuery } from 'react-query';
+
+import {
+ useNamespace,
+ useOkapiKy,
+} from '@folio/stripes/core';
+import { getFullName } from '@folio/stripes/util';
+import {
+ fetchAcqUnitsByIds,
+ getVersionMetadata,
+ useOrganization,
+ useUsersBatch,
+} from '@folio/stripes-acq-components';
+import { currenciesByCode } from '@folio/stripes/components';
+
+import {
+ useContactsByIds,
+ useInterfacesByIds,
+} from '../../../../common/hooks';
+
+const getUniqItems = (arr) => (
+ flow(
+ uniq,
+ filter(Boolean),
+ )(arr)
+);
+
+export const useSelectedOrganizationVersion = ({ versionId, versions, snapshotPath }, options = {}) => {
+ const intl = useIntl();
+ const ky = useOkapiKy();
+ const [namespace] = useNamespace({ key: 'organization-version-data' });
+
+ const deletedRecordLabel = intl.formatMessage({ id: 'stripes-acq-components.versionHistory.deletedRecord' });
+
+ const currentVersion = useMemo(() => (
+ versions?.find(({ id }) => id === versionId)
+ ), [versionId, versions]);
+
+ const versionSnapshot = useMemo(() => (
+ get(snapshotPath, currentVersion)
+ ), [snapshotPath, currentVersion]);
+
+ const {
+ organization,
+ isLoading: isOrganizationLoading,
+ } = useOrganization(currentVersion?.organizationId);
+
+ const metadata = useMemo(() => getVersionMetadata(currentVersion, organization), [currentVersion, organization]);
+ const createdByUserId = metadata?.createdByUserId;
+
+ const versionUserIds = useMemo(() => getUniqItems([createdByUserId]), [createdByUserId]);
+ const {
+ users,
+ isLoading: isUsersLoading,
+ } = useUsersBatch(versionUserIds);
+
+ const {
+ contacts,
+ isLoading: isContactsLoading,
+ } = useContactsByIds(versionSnapshot?.contacts);
+
+ const {
+ interfaces,
+ isLoading: isInterfacesLoading,
+ } = useInterfacesByIds(versionSnapshot?.interfaces);
+
+ const {
+ isLoading: isVersionDataLoading,
+ data = {},
+ } = useQuery({
+ queryKey: [namespace, versionId, versionSnapshot?.id],
+ queryFn: async () => {
+ const acqUnitsIds = [
+ ...get('acqUnitIds', versionSnapshot, []),
+ ...flow(
+ get('accounts'),
+ flatMap('acqUnitIds'),
+ uniq,
+ )(versionSnapshot),
+ ];
+
+ const [acqUnitsMap] = await Promise.all([
+ fetchAcqUnitsByIds(ky)(acqUnitsIds).then(keyBy('id')),
+ ]);
+
+ const vendorCurrenciesValue = versionSnapshot?.vendorCurrencies?.map(currency => {
+ const currencyInfo = currenciesByCode[currency];
+
+ return currencyInfo ? `${currencyInfo.currency} (${currencyInfo.code})` : currency;
+ }).join(', ');
+
+ return {
+ ...versionSnapshot,
+ accounts: versionSnapshot?.accounts?.map((account) => ({
+ ...account,
+ acqUnits: account?.acqUnitIds?.map((acqUnitId) => acqUnitsMap[acqUnitId]?.name || deletedRecordLabel),
+ })),
+ acqUnits: acqUnitsIds.map(acqUnitsId => acqUnitsMap[acqUnitsId]?.name || deletedRecordLabel).join(', '),
+ alternativeNames: versionSnapshot?.aliases?.map(({ value }) => value).join(', '),
+ vendorCurrenciesValue,
+ metadata,
+ };
+ },
+ enabled: Boolean(versionId && organization?.id),
+ ...options,
+ });
+
+ const selectedVersion = useMemo(() => {
+ const versionUsersMap = keyBy('id', users);
+
+ const createdByUser = versionUsersMap[createdByUserId]
+ ? getFullName(versionUsersMap[createdByUserId])
+ : deletedRecordLabel;
+
+ return {
+ ...data,
+ createdByUser: createdByUserId && createdByUser,
+ contactsList: contacts,
+ interfacesList: interfaces,
+ };
+ }, [users, createdByUserId, deletedRecordLabel, data, contacts, interfaces]);
+
+ const isLoading = (
+ isOrganizationLoading
+ || isUsersLoading
+ || isVersionDataLoading
+ || isContactsLoading
+ || isInterfacesLoading
+ );
+
+ return {
+ isLoading,
+ selectedVersion,
+ };
+};
diff --git a/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.test.js b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.test.js
new file mode 100644
index 00000000..752aff62
--- /dev/null
+++ b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.test.js
@@ -0,0 +1,89 @@
+import get from 'lodash/get';
+import {
+ QueryClient,
+ QueryClientProvider,
+} from 'react-query';
+
+import {
+ renderHook,
+ waitFor,
+} from '@folio/jest-config-stripes/testing-library/react';
+import {
+ fetchAcqUnitsByIds,
+ getVersionMetadata,
+ useOrganization,
+ useUsersBatch,
+} from '@folio/stripes-acq-components';
+
+import { organizationAuditEvent } from 'fixtures';
+import {
+ useContactsByIds,
+ useInterfacesByIds,
+} from '../../../../common/hooks';
+import { useSelectedOrganizationVersion } from './useSelectedOrganizationVersion';
+
+jest.mock('@folio/stripes-acq-components', () => ({
+ ...jest.requireActual('@folio/stripes-acq-components'),
+ fetchAcqUnitsByIds: jest.fn(),
+ getVersionMetadata: jest.fn(),
+ useOrganization: jest.fn(),
+ useUsersBatch: jest.fn(),
+}));
+jest.mock('../../../../common/hooks', () => ({
+ ...jest.requireActual('../../../../common/hooks'),
+ useContactsByIds: jest.fn(),
+ useInterfacesByIds: jest.fn(),
+}));
+
+const versionId = organizationAuditEvent.id;
+const versions = [organizationAuditEvent];
+const snapshotPath = 'organizationSnapshot.map';
+
+const contacts = [{ id: 'contact1' }];
+const interfaces = [{ id: 'interface1' }];
+const organization = { id: 'org1' };
+const users = [{ id: 'user1', personal: { firstName: 'John', lastName: 'Doe' } }];
+
+const queryClient = new QueryClient();
+const wrapper = ({ children }) => (
+
+ {children}
+
+);
+
+describe('useSelectedOrganizationVersion', () => {
+ beforeEach(() => {
+ fetchAcqUnitsByIds.mockReturnValue(() => Promise.resolve([{ id: 'acq-unit-id' }]));
+ useOrganization.mockReturnValue({ organization, isLoading: false });
+ useUsersBatch.mockReturnValue({ users, isLoading: false });
+ useContactsByIds.mockReturnValue({ contacts, isLoading: false });
+ useInterfacesByIds.mockReturnValue({ interfaces, isLoading: false });
+ });
+
+ afterEach(() => {
+ jest.clearAllMocks();
+ });
+
+ it('should return selected version data', async () => {
+ const { result } = renderHook(
+ () => useSelectedOrganizationVersion({ versionId, versions, snapshotPath }),
+ { wrapper },
+ );
+
+ await waitFor(() => expect(result.current.isLoading).toBeFalsy());
+
+ expect(result.current.selectedVersion).toEqual({
+ ...get(organizationAuditEvent, snapshotPath),
+ accounts: get(organizationAuditEvent, `${snapshotPath}.accounts`).map(acc => ({
+ ...acc,
+ acqUnits: [],
+ })),
+ acqUnits: '',
+ alternativeNames: 'Amazon',
+ contactsList: contacts,
+ interfacesList: interfaces,
+ vendorCurrenciesValue: 'US Dollar (USD)',
+ metadata: getVersionMetadata(organizationAuditEvent, organization),
+ });
+ });
+});
diff --git a/src/common/constants/vendorCategories.js b/src/common/constants/vendorCategories.js
index 33812d25..c9d26ab0 100644
--- a/src/common/constants/vendorCategories.js
+++ b/src/common/constants/vendorCategories.js
@@ -1,3 +1,4 @@
+export const UNCATEGORIZED_ID = 'uncategorized';
export const UNCATEGORIZED_VALUE = 'Uncategorized';
export const VENDOR_DEFAULT_CATEGORIES = {
diff --git a/src/common/hooks/index.js b/src/common/hooks/index.js
index 9b6c0fd4..d866e888 100644
--- a/src/common/hooks/index.js
+++ b/src/common/hooks/index.js
@@ -3,10 +3,13 @@ export * from './useBankingAccountTypes';
export * from './useBankingInformationMutation';
export * from './useBankingInformationSettings';
export * from './useCategories';
+export * from './useContactsByIds';
export * from './useEventEmitter';
export * from './useIntegrationConfig';
export * from './useIntegrationConfigMutation';
+export * from './useInterfacesByIds';
export * from './useLinkedAgreements';
export * from './useOrganizationBankingInformation';
export * from './useTranslatedCategories';
export * from './useTypes';
+export * from './useVersionWrappedRowFormatter';
diff --git a/src/common/hooks/useCategories/useCategories.js b/src/common/hooks/useCategories/useCategories.js
index 8cd3878b..a4349b2f 100644
--- a/src/common/hooks/useCategories/useCategories.js
+++ b/src/common/hooks/useCategories/useCategories.js
@@ -4,7 +4,10 @@ import {
useNamespace,
useOkapiKy,
} from '@folio/stripes/core';
-import { LIMIT_MAX } from '@folio/stripes-acq-components';
+import {
+ ALL_RECORDS_CQL,
+ LIMIT_MAX,
+} from '@folio/stripes-acq-components';
import { CATEGORIES_API } from '../../constants';
import { useTranslatedCategories } from '../useTranslatedCategories';
@@ -17,7 +20,7 @@ export const useCategories = (options = {}) => {
const searchParams = {
limit: LIMIT_MAX,
- query: 'cql.allRecords=1',
+ query: ALL_RECORDS_CQL,
};
const {
@@ -26,7 +29,7 @@ export const useCategories = (options = {}) => {
isLoading,
} = useQuery(
[namespace],
- () => ky.get(CATEGORIES_API, { searchParams }).json(),
+ ({ signal }) => ky.get(CATEGORIES_API, { searchParams, signal }).json(),
options,
);
diff --git a/src/common/hooks/useContactsByIds/index.js b/src/common/hooks/useContactsByIds/index.js
new file mode 100644
index 00000000..b8a06c06
--- /dev/null
+++ b/src/common/hooks/useContactsByIds/index.js
@@ -0,0 +1 @@
+export { useContactsByIds } from './useContactsByIds';
diff --git a/src/common/hooks/useContactsByIds/useContactsByIds.js b/src/common/hooks/useContactsByIds/useContactsByIds.js
new file mode 100644
index 00000000..74639eed
--- /dev/null
+++ b/src/common/hooks/useContactsByIds/useContactsByIds.js
@@ -0,0 +1,46 @@
+import { useQuery } from 'react-query';
+
+import {
+ useNamespace,
+ useOkapiKy,
+} from '@folio/stripes/core';
+import { batchFetch } from '@folio/stripes-acq-components';
+
+import { CONTACTS_API } from '../../constants';
+
+const DEFAULT_DATA = [];
+
+export const useContactsByIds = (contactIds, options = {}) => {
+ const {
+ enabled = true,
+ ...queryOptions
+ } = options;
+
+ const ky = useOkapiKy();
+ const [namespace] = useNamespace({ key: 'contacts' });
+
+ const {
+ data,
+ isFetching,
+ isLoading,
+ } = useQuery({
+ queryKey: [namespace, contactIds],
+ queryFn: ({ signal }) => batchFetch(
+ {
+ GET: ({ params: searchParams }) => {
+ return ky.get(CONTACTS_API, { searchParams, signal }).json().then(({ contacts }) => contacts);
+ },
+ },
+ contactIds,
+ ),
+ enabled: Boolean(enabled && contactIds?.length),
+ ...queryOptions,
+ });
+
+ return ({
+ contacts: data || DEFAULT_DATA,
+ totalRecords: data?.length,
+ isFetching,
+ isLoading,
+ });
+};
diff --git a/src/common/hooks/useInterfacesByIds/index.js b/src/common/hooks/useInterfacesByIds/index.js
new file mode 100644
index 00000000..e389bcf9
--- /dev/null
+++ b/src/common/hooks/useInterfacesByIds/index.js
@@ -0,0 +1 @@
+export { useInterfacesByIds } from './useInterfacesByIds';
diff --git a/src/common/hooks/useInterfacesByIds/useInterfacesByIds.js b/src/common/hooks/useInterfacesByIds/useInterfacesByIds.js
new file mode 100644
index 00000000..a11d5452
--- /dev/null
+++ b/src/common/hooks/useInterfacesByIds/useInterfacesByIds.js
@@ -0,0 +1,46 @@
+import { useQuery } from 'react-query';
+
+import {
+ useNamespace,
+ useOkapiKy,
+} from '@folio/stripes/core';
+import { batchFetch } from '@folio/stripes-acq-components';
+
+import { INTERFACES_API } from '../../constants';
+
+const DEFAULT_DATA = [];
+
+export const useInterfacesByIds = (interfaceIds, options = {}) => {
+ const {
+ enabled = true,
+ ...queryOptions
+ } = options;
+
+ const ky = useOkapiKy();
+ const [namespace] = useNamespace({ key: 'interfaces' });
+
+ const {
+ data,
+ isFetching,
+ isLoading,
+ } = useQuery({
+ queryKey: [namespace, interfaceIds],
+ queryFn: ({ signal }) => batchFetch(
+ {
+ GET: ({ params: searchParams }) => {
+ return ky.get(INTERFACES_API, { searchParams, signal }).json().then(({ interfaces }) => interfaces);
+ },
+ },
+ interfaceIds,
+ ),
+ enabled: Boolean(enabled && interfaceIds?.length),
+ ...queryOptions,
+ });
+
+ return {
+ interfaces: data || DEFAULT_DATA,
+ totalRecords: data?.length,
+ isFetching,
+ isLoading,
+ };
+};
diff --git a/src/common/hooks/useVersionWrappedRowFormatter/index.js b/src/common/hooks/useVersionWrappedRowFormatter/index.js
new file mode 100644
index 00000000..dcc8b135
--- /dev/null
+++ b/src/common/hooks/useVersionWrappedRowFormatter/index.js
@@ -0,0 +1 @@
+export { useVersionWrappedRowFormatter } from './useVersionWrappedRowFormatter';
diff --git a/src/common/hooks/useVersionWrappedRowFormatter/styles.css b/src/common/hooks/useVersionWrappedRowFormatter/styles.css
new file mode 100644
index 00000000..69d7cc00
--- /dev/null
+++ b/src/common/hooks/useVersionWrappedRowFormatter/styles.css
@@ -0,0 +1,6 @@
+.mark {
+ &.version-wrapped.version-row-formatted {
+ background-color: mark;
+ margin: 0.15rem 0;
+ }
+}
diff --git a/src/common/hooks/useVersionWrappedRowFormatter/useVersionWrappedRowFormatter.js b/src/common/hooks/useVersionWrappedRowFormatter/useVersionWrappedRowFormatter.js
new file mode 100644
index 00000000..d997e92a
--- /dev/null
+++ b/src/common/hooks/useVersionWrappedRowFormatter/useVersionWrappedRowFormatter.js
@@ -0,0 +1,55 @@
+import {
+ useCallback,
+ useContext,
+} from 'react';
+
+import { DefaultMCLRowFormatter } from '@folio/stripes/components';
+import { VersionViewContext } from '@folio/stripes-acq-components';
+
+import css from './styles.css';
+
+const getVersionWrappedRowFormatter = ({
+ baseRowFormatter = DefaultMCLRowFormatter,
+ row,
+ name,
+ paths,
+}) => {
+ const {
+ rowClass,
+ rowIndex,
+ ...props
+ } = row;
+
+ const isUpdated = paths?.includes(`${name}[${rowIndex}]`);
+
+ return baseRowFormatter({
+ ...props,
+ rowClass: [
+ css['version-wrapped'],
+ css['version-row-formatted'],
+ rowClass,
+ isUpdated ? css.mark : '',
+ ].join(' '),
+ rowIndex,
+ });
+};
+
+export const useVersionWrappedRowFormatter = ({
+ baseRowFormatter,
+ name,
+}) => {
+ const versionContext = useContext(VersionViewContext);
+
+ const rowFormatter = useCallback((row) => {
+ if (!versionContext || !name) return baseRowFormatter;
+
+ return getVersionWrappedRowFormatter({
+ baseRowFormatter,
+ row,
+ name,
+ paths: versionContext.paths,
+ });
+ }, [baseRowFormatter, name, versionContext]);
+
+ return rowFormatter;
+};
diff --git a/src/common/hooks/useVersionWrappedRowFormatter/useVersionWrappedRowFormatter.test.js b/src/common/hooks/useVersionWrappedRowFormatter/useVersionWrappedRowFormatter.test.js
new file mode 100644
index 00000000..f8a9b43b
--- /dev/null
+++ b/src/common/hooks/useVersionWrappedRowFormatter/useVersionWrappedRowFormatter.test.js
@@ -0,0 +1,41 @@
+import { renderHook } from '@folio/jest-config-stripes/testing-library/react';
+
+import { VersionViewContext } from '@folio/stripes-acq-components';
+
+import { useVersionWrappedRowFormatter } from './useVersionWrappedRowFormatter';
+
+const versionViewContext = { paths: ['testName[0]'] };
+
+const getWrapper = (contextValue = {}) => ({ children }) => (
+
+ {children}
+
+);
+
+const mockBaseRowFormatter = ({ rowClass }) => ({ rowClass });
+
+describe('useVersionWrappedRowFormatter', () => {
+ it('should return version wrapped row formatter if versionContext and name are provided', () => {
+ const { result } = renderHook(() => useVersionWrappedRowFormatter({
+ baseRowFormatter: mockBaseRowFormatter,
+ name: 'testName',
+ }), { wrapper: getWrapper() });
+
+ const rowFormatter = result.current;
+ const row = { rowClass: 'testClass', rowIndex: 0 };
+
+ expect(rowFormatter(row).rowClass.includes('mark')).toBeTruthy();
+ });
+
+ it('should not add mark class if row is not updated', () => {
+ const { result } = renderHook(() => useVersionWrappedRowFormatter({
+ baseRowFormatter: mockBaseRowFormatter,
+ name: 'testName',
+ }), { wrapper: getWrapper({ paths: ['otherName[0]'] }) });
+
+ const rowFormatter = result.current;
+ const row = { rowClass: 'testClass', rowIndex: 0 };
+
+ expect(rowFormatter(row).rowClass.includes('mark')).toBeFalsy();
+ });
+});
diff --git a/translations/ui-organizations/en.json b/translations/ui-organizations/en.json
index 2181146c..083d0197 100644
--- a/translations/ui-organizations/en.json
+++ b/translations/ui-organizations/en.json
@@ -449,6 +449,7 @@
"versionHistory.field.accessProvider": "Access provider",
"versionHistory.field.governmental": "Governmental",
"versionHistory.field.licensor": "Licensor",
+ "versionHistory.field.edi.notes": "Notes (EDI)",
"versionHistory.field.edi.prorateTax": "Prorate tax",
"versionHistory.field.edi.prorateFees": "Prorate fees",
"versionHistory.field.edi.ediJob.sendToEmails": "Send to emails",
@@ -456,6 +457,26 @@
"versionHistory.field.edi.ediJob.notifyInvoiceOnly": "Notify invoice only",
"versionHistory.field.edi.ediJob.notifyErrorOnly": "Notify error only",
"versionHistory.field.edi.ediJob.schedulingNotes": "Scheduling notes",
+ "versionHistory.field.address.isPrimary": "Primary (Address)",
+ "versionHistory.field.address.categories": "Categories (Address)",
+ "versionHistory.field.address.language": "Language (Address)",
+ "versionHistory.field.phoneNumbers.isPrimary": "Primary (Phone number)",
+ "versionHistory.field.phoneNumbers.categories": "Categories (Phone number)",
+ "versionHistory.field.phoneNumbers.language": "Language (Phone number)",
+ "versionHistory.field.phoneNumbers.type": "Type (Phone number)",
+ "versionHistory.field.emails.description": "Description (Email)",
+ "versionHistory.field.emails.isPrimary": "Primary (Email)",
+ "versionHistory.field.emails.categories": "Categories (Email)",
+ "versionHistory.field.emails.language": "Language (Email)",
+ "versionHistory.field.urls.description": "Description (URL)",
+ "versionHistory.field.urls.language": "Language (URL)",
+ "versionHistory.field.urls.isPrimary": "Primary (URL)",
+ "versionHistory.field.urls.categories": "Categories (URL)",
+ "versionHistory.field.agreements.name": "Name (Agreement)",
+ "versionHistory.field.agreements.notes": "Notes (Agreement)",
+ "versionHistory.field.accounts.name": "Name (Account)",
+ "versionHistory.field.accounts.description": "Description (Account)",
+ "versionHistory.field.accounts.notes": "Notes (Account)",
"settings.categories": "Categories",
"settings.categories.cannotDeleteTermHeader": "Cannot delete category",