From 40a886bf598a5a3d5752c4e68760c11ec47cd5d5 Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Mon, 25 Nov 2024 20:33:50 +0400 Subject: [PATCH 1/8] UIORGS-356 Show in version history record view, which fields have been edited --- CHANGELOG.md | 2 + src/ContactPeople/ContactPerson/index.js | 1 + .../OrganizationVersion.js | 17 +- .../OrganizationVersionView.js | 149 ++++++++++++++++++ .../OrganizationVersionView/index.js | 1 + .../OrganizationAccountsVersionView.js | 119 ++++++++++++++ .../OrganizationAccountsVersionView/index.js | 1 + .../OrganizationContactInfoVersionView.js | 104 ++++++++++++ .../components/ContactAddressesVersionView.js | 102 ++++++++++++ .../ContactPersonEmailsVersionView.js | 86 ++++++++++ .../ContactPersonPhonesVersionView.js | 88 +++++++++++ .../ContactPersonURLsVersionView.js | 94 +++++++++++ .../components/index.js | 4 + .../index.js | 1 + .../OrganizationContactPeopleVersionView.js | 79 ++++++++++ .../index.js | 1 + .../OrganizationInterfacesVersionView.js | 63 ++++++++ .../index.js | 1 + .../OrganizationSummaryVersionView.js | 130 +++++++++++++++ .../OrganizationSummaryVersionView/index.js | 1 + .../OrganizationVendorInfoVersionView.js | 135 ++++++++++++++++ .../index.js | 1 + .../OrganizationVendorTermsVersionView.js | 63 ++++++++ .../index.js | 1 + .../OrganizationVersion/components/index.js | 7 + .../getOrganizationFieldsLabelMap.js | 25 +-- .../OrganizationVersion/hooks/index.js | 1 + .../useSelectedOrganizationVersion/index.js | 1 + .../useSelectedOrganizationVersion.js | 147 +++++++++++++++++ src/common/constants/vendorCategories.js | 1 + src/common/hooks/index.js | 3 + .../hooks/useCategories/useCategories.js | 9 +- src/common/hooks/useContactsByIds/index.js | 1 + .../useContactsByIds/useContactsByIds.js | 46 ++++++ src/common/hooks/useInterfacesByIds/index.js | 1 + .../useInterfacesByIds/useInterfacesByIds.js | 46 ++++++ .../useVersionWrappedRowFormatter/index.js | 1 + .../useVersionWrappedRowFormatter/styles.css | 6 + .../useVersionWrappedRowFormatter.js | 55 +++++++ 39 files changed, 1576 insertions(+), 18 deletions(-) create mode 100644 src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.js create mode 100644 src/Organizations/OrganizationVersion/OrganizationVersionView/index.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/index.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/OrganizationContactInfoVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonEmailsVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonURLsVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/index.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/index.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/index.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationInterfacesVersionView/OrganizationInterfacesVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationInterfacesVersionView/index.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationSummaryVersionView/OrganizationSummaryVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationSummaryVersionView/index.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationVendorInfoVersionView/OrganizationVendorInfoVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationVendorInfoVersionView/index.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationVendorTermsVersionView/OrganizationVendorTermsVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationVendorTermsVersionView/index.js create mode 100644 src/Organizations/OrganizationVersion/components/index.js create mode 100644 src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/index.js create mode 100644 src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js create mode 100644 src/common/hooks/useContactsByIds/index.js create mode 100644 src/common/hooks/useContactsByIds/useContactsByIds.js create mode 100644 src/common/hooks/useInterfacesByIds/index.js create mode 100644 src/common/hooks/useInterfacesByIds/useInterfacesByIds.js create mode 100644 src/common/hooks/useVersionWrappedRowFormatter/index.js create mode 100644 src/common/hooks/useVersionWrappedRowFormatter/styles.css create mode 100644 src/common/hooks/useVersionWrappedRowFormatter/useVersionWrappedRowFormatter.js 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/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 accordionStatusRef = useRef(); + + const shortcuts = [ + { + name: 'expandAllSections', + handler: (e) => expandAllSections(e, accordionStatusRef), + }, + { + name: 'collapseAllSections', + handler: (e) => collapseAllSections(e, accordionStatusRef), + }, + ]; + + console.log('version', version); + + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + { + version?.isVendor && ( + <> + + + + + + + + + + + + + ) + } + + + + ); +}; + +OrganizationVersionView.propTypes = { + version: PropTypes.object, +}; 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..f93ad5a0 --- /dev/null +++ b/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js @@ -0,0 +1,119 @@ +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..b9b2d71f --- /dev/null +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/OrganizationContactInfoVersionView.js @@ -0,0 +1,104 @@ +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..72abba41 --- /dev/null +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js @@ -0,0 +1,102 @@ +import PropTypes from 'prop-types'; +import { useContext } from 'react'; +import { FormattedMessage } from 'react-intl'; + +import { + Card, + Col, + Row, +} from '@folio/stripes/components'; +import { + VersionKeyValue, + VersionViewContext, +} from '@folio/stripes-acq-components'; + +export const ContactAddressesVersionView = ({ addresses }) => { + const versionContext = useContext(VersionViewContext); + + return ( + + + {addresses?.map((address) => { + const isPrimaryValue = ( +

+ +

+ ); + const headerStart = versionContext?.paths?.includes(`${address?._initialFieldPath}.isPrimary`) + ? {isPrimaryValue} + : isPrimaryValue; + + 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/ContactPersonEmailsVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonEmailsVersionView.js new file mode 100644 index 00000000..65b6d1b3 --- /dev/null +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonEmailsVersionView.js @@ -0,0 +1,86 @@ +import PropTypes from 'prop-types'; +import { useContext } from 'react'; +import { FormattedMessage } from 'react-intl'; + +import { + Card, + Col, + Row, +} from '@folio/stripes/components'; +import { + LANG_LABEL_BY_CODE, + VersionKeyValue, + VersionViewContext, +} from '@folio/stripes-acq-components'; + +import { ContactPersonSection } from '../../../../../ContactPeople/ContactPerson'; + +export const ContactPersonEmailsVersionView = ({ emails }) => { + const versionContext = useContext(VersionViewContext); + + if (!emails?.length) return null; + + const renderBody = () => ( + + + {emails?.map((email) => { + const isPrimaryValue = ( +

+ +

+ ); + const headerStart = versionContext?.paths?.includes(`${email?._initialFieldPath}.isPrimary`) + ? {isPrimaryValue} + : isPrimaryValue; + + 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..7b4766a3 --- /dev/null +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js @@ -0,0 +1,88 @@ +import PropTypes from 'prop-types'; +import { useContext } from 'react'; +import { FormattedMessage } from 'react-intl'; + +import { + Card, + Col, + Row, +} from '@folio/stripes/components'; +import { + LANG_LABEL_BY_CODE, + VersionKeyValue, + VersionViewContext, +} from '@folio/stripes-acq-components'; + +import { ContactPersonSection } from '../../../../../ContactPeople/ContactPerson'; + +export const ContactPersonPhonesVersionView = ({ phones }) => { + const versionContext = useContext(VersionViewContext); + + if (!phones?.length) return null; + + const renderBody = () => ( + ( + + + {phones?.map((phone) => { + const isPrimaryValue = ( +

+ +

+ ); + const headerStart = versionContext?.paths?.includes(`${phone?._initialFieldPath}.isPrimary`) + ? {isPrimaryValue} + : isPrimaryValue; + + 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..45477cf9 --- /dev/null +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonURLsVersionView.js @@ -0,0 +1,94 @@ +import PropTypes from 'prop-types'; +import { useContext } from 'react'; +import { FormattedMessage } from 'react-intl'; + +import { + Card, + Col, + Row, + TextLink, +} from '@folio/stripes/components'; +import { + LANG_LABEL_BY_CODE, + VersionKeyValue, + VersionViewContext, +} from '@folio/stripes-acq-components'; + +import { ContactPersonSection } from '../../../../../ContactPeople/ContactPerson'; + +export const ContactPersonURLsVersionView = ({ urls }) => { + const versionContext = useContext(VersionViewContext); + + if (!urls?.length) return null; + + const renderBody = () => ( + + + {urls?.map((url) => { + const isPrimaryValue = ( +

+ +

+ ); + const headerStart = versionContext?.paths?.includes(`${url?._initialFieldPath}.isPrimary`) + ? {isPrimaryValue} + : isPrimaryValue; + + 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..34ce850c --- /dev/null +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js @@ -0,0 +1,79 @@ +import get from 'lodash/get'; +import find from 'lodash/find'; +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..5c4abddd --- /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..4b52c133 100644 --- a/src/Organizations/OrganizationVersion/getOrganizationFieldsLabelMap.js +++ b/src/Organizations/OrganizationVersion/getOrganizationFieldsLabelMap.js @@ -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..e9ec136d --- /dev/null +++ b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js @@ -0,0 +1,147 @@ +import get from 'lodash/fp/get'; +import filter from 'lodash/fp/filter'; +import flatMap from 'lodash/fp/flatMap'; +import flow from 'lodash/fp/flow'; +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( + [namespace, versionId, versionSnapshot?.id], + 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/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..d8f57d25 --- /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('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..c89e8861 --- /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('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..32c37302 --- /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; + } +} \ No newline at end of file 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; +}; From 3d7fc762d14f9dad261df2dfe538da5cd991e1c9 Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Tue, 26 Nov 2024 11:47:11 +0400 Subject: [PATCH 2/8] update card header --- .../OrganizationVersionView.js | 2 -- .../OrganizationContactInfoVersionView.js | 10 ++----- .../components/ContactAddressesVersionView.js | 28 ++++++++----------- .../ContactCardHeaderVersionView.css | 7 +++++ .../ContactCardHeaderVersionView.js | 26 +++++++++++++++++ .../ContactCardHeaderVersionView/index.js | 1 + .../ContactPersonEmailsVersionView.js | 23 ++++++--------- .../ContactPersonPhonesVersionView.js | 23 ++++++--------- .../ContactPersonURLsVersionView.js | 23 ++++++--------- 9 files changed, 74 insertions(+), 69 deletions(-) create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.css create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.js create mode 100644 src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/index.js diff --git a/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.js b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.js index ee667272..d96763da 100644 --- a/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.js +++ b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.js @@ -54,8 +54,6 @@ export const OrganizationVersionView = ({ version }) => { }, ]; - console.log('version', version); - return ( - - + + ))} diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js index 72abba41..db641a15 100644 --- a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js @@ -1,5 +1,4 @@ import PropTypes from 'prop-types'; -import { useContext } from 'react'; import { FormattedMessage } from 'react-intl'; import { @@ -7,29 +6,24 @@ import { Col, Row, } from '@folio/stripes/components'; -import { - VersionKeyValue, - VersionViewContext, -} from '@folio/stripes-acq-components'; +import { VersionKeyValue } from '@folio/stripes-acq-components'; -export const ContactAddressesVersionView = ({ addresses }) => { - const versionContext = useContext(VersionViewContext); +import { ContactCardHeaderVersionView } from './ContactCardHeaderVersionView'; +export const ContactAddressesVersionView = ({ addresses }) => { return ( {addresses?.map((address) => { - const isPrimaryValue = ( -

- -

- ); - const headerStart = versionContext?.paths?.includes(`${address?._initialFieldPath}.isPrimary`) - ? {isPrimaryValue} - : isPrimaryValue; - return ( - + + )} + > { + 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 index 65b6d1b3..46cf494e 100644 --- a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonEmailsVersionView.js +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonEmailsVersionView.js @@ -1,5 +1,4 @@ import PropTypes from 'prop-types'; -import { useContext } from 'react'; import { FormattedMessage } from 'react-intl'; import { @@ -10,31 +9,27 @@ import { import { LANG_LABEL_BY_CODE, VersionKeyValue, - VersionViewContext, } from '@folio/stripes-acq-components'; import { ContactPersonSection } from '../../../../../ContactPeople/ContactPerson'; +import { ContactCardHeaderVersionView } from './ContactCardHeaderVersionView'; export const ContactPersonEmailsVersionView = ({ emails }) => { - const versionContext = useContext(VersionViewContext); - if (!emails?.length) return null; const renderBody = () => ( {emails?.map((email) => { - const isPrimaryValue = ( -

- -

- ); - const headerStart = versionContext?.paths?.includes(`${email?._initialFieldPath}.isPrimary`) - ? {isPrimaryValue} - : isPrimaryValue; - return ( - + + )} + > { - const versionContext = useContext(VersionViewContext); - if (!phones?.length) return null; const renderBody = () => ( @@ -25,17 +22,15 @@ export const ContactPersonPhonesVersionView = ({ phones }) => { {phones?.map((phone) => { - const isPrimaryValue = ( -

- -

- ); - const headerStart = versionContext?.paths?.includes(`${phone?._initialFieldPath}.isPrimary`) - ? {isPrimaryValue} - : isPrimaryValue; - return ( - + + )} + > { - const versionContext = useContext(VersionViewContext); - if (!urls?.length) return null; const renderBody = () => ( {urls?.map((url) => { - const isPrimaryValue = ( -

- -

- ); - const headerStart = versionContext?.paths?.includes(`${url?._initialFieldPath}.isPrimary`) - ? {isPrimaryValue} - : isPrimaryValue; - return ( - + + )} + > Date: Tue, 26 Nov 2024 13:06:15 +0400 Subject: [PATCH 3/8] Update labels mapping --- .../getOrganizationFieldsLabelMap.js | 42 +++++++++---------- translations/ui-organizations/en.json | 21 ++++++++++ 2 files changed, 42 insertions(+), 21 deletions(-) diff --git a/src/Organizations/OrganizationVersion/getOrganizationFieldsLabelMap.js b/src/Organizations/OrganizationVersion/getOrganizationFieldsLabelMap.js index 4b52c133..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', 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", From 570e7f409586d5408a00b9ae396ca49d01a798ba Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Tue, 26 Nov 2024 15:01:08 +0400 Subject: [PATCH 4/8] fix old test --- .../OrganizationBankingInfoForm.test.js | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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(); }); }); From e14ad1b8f767cce21e03e0d377fefecd071de0e0 Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Tue, 26 Nov 2024 16:53:34 +0400 Subject: [PATCH 5/8] provide keys for arrays items and add tests --- .../OrganizationVersion.test.js | 2 +- .../OrganizationVersionView.test.js | 54 +++++++++++++++++++ .../OrganizationAccountsVersionView.js | 5 +- .../OrganizationContactInfoVersionView.js | 2 +- .../components/ContactAddressesVersionView.js | 1 + .../ContactPersonEmailsVersionView.js | 1 + .../ContactPersonPhonesVersionView.js | 1 + .../ContactPersonURLsVersionView.js | 1 + .../OrganizationVendorTermsVersionView.js | 2 +- 9 files changed, 65 insertions(+), 4 deletions(-) create mode 100644 src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js diff --git a/src/Organizations/OrganizationVersion/OrganizationVersion.test.js b/src/Organizations/OrganizationVersion/OrganizationVersion.test.js index 2e18d322..594c149e 100644 --- a/src/Organizations/OrganizationVersion/OrganizationVersion.test.js +++ b/src/Organizations/OrganizationVersion/OrganizationVersion.test.js @@ -35,7 +35,7 @@ jest.mock('@folio/stripes-acq-components', () => ({ const { organizationSnapshot, ...auditEvent } = organizationAuditEvent; const latestSnapshot = { - ...organizationSnapshot, + ...organizationSnapshot.map, edition: 'Second edition', }; const originalSnapshot = { ...organizationSnapshot }; diff --git a/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js new file mode 100644 index 00000000..a54824b5 --- /dev/null +++ b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js @@ -0,0 +1,54 @@ +import { + QueryClient, + QueryClientProvider, +} from 'react-query'; +import { MemoryRouter } from 'react-router-dom'; + +import { + render, + screen, +} from '@folio/jest-config-stripes/testing-library/react'; + +import { + organization, + organizationAuditEvent, +} from 'fixtures'; +import { ORGANIZATION_VERSIONS_VIEW_ROUTE } from '../../../common/constants'; +import { OrganizationVersionView } from './OrganizationVersionView'; + +const { organizationSnapshot } = organizationAuditEvent; + +const queryClient = new QueryClient(); +const wrapper = ({ children }) => ( + + + {children} + + +); + +const renderOrganizationVersionView = (props = {}) => render( + , + { wrapper }, +); + +describe('OrganizationVersion', () => { + 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/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js index f93ad5a0..b09ebc50 100644 --- a/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js +++ b/src/Organizations/OrganizationVersion/components/OrganizationAccountsVersionView/OrganizationAccountsVersionView.js @@ -25,7 +25,10 @@ export const OrganizationAccountsVersionView = ({ name, version }) => { <> {version?.accounts?.map((account, indx) => { return ( - + { return paths.reduce((result, path) => { - const target = get(result, path); + const target = get(result, path, []); target.forEach((item, indx) => { item._initialFieldPath = `${path}[${indx}]`; diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js index db641a15..aac1908b 100644 --- a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactAddressesVersionView.js @@ -17,6 +17,7 @@ export const ContactAddressesVersionView = ({ addresses }) => { {addresses?.map((address) => { return ( { {emails?.map((email) => { return ( { {phones?.map((phone) => { return ( { {urls?.map((url) => { return ( { <> {version?.agreements?.map((agreement, indx) => { return ( - + Date: Tue, 26 Nov 2024 18:04:38 +0400 Subject: [PATCH 6/8] add tests --- .../useSelectedOrganizationVersion.js | 18 ++-- .../useSelectedOrganizationVersion.test.js | 89 +++++++++++++++++++ 2 files changed, 96 insertions(+), 11 deletions(-) create mode 100644 src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.test.js diff --git a/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js index e9ec136d..74647f5d 100644 --- a/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js +++ b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js @@ -75,9 +75,9 @@ export const useSelectedOrganizationVersion = ({ versionId, versions, snapshotPa const { isLoading: isVersionDataLoading, data = {}, - } = useQuery( - [namespace, versionId, versionSnapshot?.id], - async () => { + } = useQuery({ + queryKey: [namespace, versionId, versionSnapshot?.id], + queryFn: async () => { const acqUnitsIds = [ ...get('acqUnitIds', versionSnapshot, []), ...flow( @@ -87,9 +87,7 @@ export const useSelectedOrganizationVersion = ({ versionId, versions, snapshotPa )(versionSnapshot), ]; - const [ - acqUnitsMap, - ] = await Promise.all([ + const [acqUnitsMap] = await Promise.all([ fetchAcqUnitsByIds(ky)(acqUnitsIds).then(keyBy('id')), ]); @@ -111,11 +109,9 @@ export const useSelectedOrganizationVersion = ({ versionId, versions, snapshotPa metadata, }; }, - { - enabled: Boolean(versionId && organization?.id), - ...options, - }, - ); + enabled: Boolean(versionId && organization?.id), + ...options, + }); const selectedVersion = useMemo(() => { const versionUsersMap = keyBy('id', users); 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), + }); + }); +}); From eddaf668e104b6ee4c65035c4626dd46d9e27cd0 Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Tue, 26 Nov 2024 18:38:05 +0400 Subject: [PATCH 7/8] updates --- .../OrganizationVersionView.test.js | 14 +++++++ .../useVersionWrappedRowFormatter/styles.css | 2 +- .../useVersionWrappedRowFormatter.test.js | 41 +++++++++++++++++++ 3 files changed, 56 insertions(+), 1 deletion(-) create mode 100644 src/common/hooks/useVersionWrappedRowFormatter/useVersionWrappedRowFormatter.test.js diff --git a/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js index a54824b5..63b2450f 100644 --- a/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js +++ b/src/Organizations/OrganizationVersion/OrganizationVersionView/OrganizationVersionView.test.js @@ -8,6 +8,7 @@ import { render, screen, } from '@folio/jest-config-stripes/testing-library/react'; +import { useCategories } from '@folio/stripes-acq-components'; import { organization, @@ -16,6 +17,11 @@ import { 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(); @@ -40,6 +46,14 @@ const renderOrganizationVersionView = (props = {}) => render( ); 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(); diff --git a/src/common/hooks/useVersionWrappedRowFormatter/styles.css b/src/common/hooks/useVersionWrappedRowFormatter/styles.css index 32c37302..69d7cc00 100644 --- a/src/common/hooks/useVersionWrappedRowFormatter/styles.css +++ b/src/common/hooks/useVersionWrappedRowFormatter/styles.css @@ -3,4 +3,4 @@ background-color: mark; margin: 0.15rem 0; } -} \ No newline at end of file +} 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(); + }); +}); From c1569f886078e5008b7d169ed3d91a3b5dbf20af Mon Sep 17 00:00:00 2001 From: Yury Saukou Date: Wed, 27 Nov 2024 10:31:16 +0400 Subject: [PATCH 8/8] resolve comments --- .../ContactCardHeaderVersionView.js | 4 +- .../ContactPersonPhonesVersionView.js | 98 +++++++++---------- .../OrganizationContactPeopleVersionView.js | 2 +- .../useSelectedOrganizationVersion.js | 2 +- .../useContactsByIds/useContactsByIds.js | 2 +- .../useInterfacesByIds/useInterfacesByIds.js | 2 +- 6 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.js index 9aaec497..ac903107 100644 --- a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.js +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactCardHeaderVersionView/ContactCardHeaderVersionView.js @@ -13,7 +13,9 @@ export const ContactCardHeaderVersionView = ({ isPrimary, name }) => { const headerStart = (

- + + +

); diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js index 826fbc08..672d1333 100644 --- a/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactInfoVersionView/components/ContactPersonPhonesVersionView.js @@ -18,57 +18,55 @@ export const ContactPersonPhonesVersionView = ({ phones }) => { if (!phones?.length) return null; const renderBody = () => ( - ( - - - {phones?.map((phone) => { - return ( - + + {phones?.map((phone) => { + return ( + + )} + > + + + } + value={phone?.phoneNumber} /> - )} - > - - - } - value={phone?.phoneNumber} - /> - - - } - value={phone?.type} - /> - - - } - value={LANG_LABEL_BY_CODE[phone?.language] || phone?.language} - /> - - - } - value={phone.categories} - multiple - /> - - - - ); - })} - - - ) + + + } + value={phone?.type} + /> + + + } + value={LANG_LABEL_BY_CODE[phone?.language] || phone?.language} + /> + + + } + value={phone.categories} + multiple + /> + +
+
+ ); + })} + +
); return ( diff --git a/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js b/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js index 34ce850c..2e40d058 100644 --- a/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js +++ b/src/Organizations/OrganizationVersion/components/OrganizationContactPeopleVersionView/OrganizationContactPeopleVersionView.js @@ -1,5 +1,5 @@ -import get from 'lodash/get'; import find from 'lodash/find'; +import get from 'lodash/get'; import PropTypes from 'prop-types'; import { useMemo } from 'react'; import { diff --git a/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js index 74647f5d..be89fbf3 100644 --- a/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js +++ b/src/Organizations/OrganizationVersion/hooks/useSelectedOrganizationVersion/useSelectedOrganizationVersion.js @@ -1,7 +1,7 @@ -import get from 'lodash/fp/get'; 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'; diff --git a/src/common/hooks/useContactsByIds/useContactsByIds.js b/src/common/hooks/useContactsByIds/useContactsByIds.js index d8f57d25..74639eed 100644 --- a/src/common/hooks/useContactsByIds/useContactsByIds.js +++ b/src/common/hooks/useContactsByIds/useContactsByIds.js @@ -17,7 +17,7 @@ export const useContactsByIds = (contactIds, options = {}) => { } = options; const ky = useOkapiKy(); - const [namespace] = useNamespace('contacts'); + const [namespace] = useNamespace({ key: 'contacts' }); const { data, diff --git a/src/common/hooks/useInterfacesByIds/useInterfacesByIds.js b/src/common/hooks/useInterfacesByIds/useInterfacesByIds.js index c89e8861..a11d5452 100644 --- a/src/common/hooks/useInterfacesByIds/useInterfacesByIds.js +++ b/src/common/hooks/useInterfacesByIds/useInterfacesByIds.js @@ -17,7 +17,7 @@ export const useInterfacesByIds = (interfaceIds, options = {}) => { } = options; const ky = useOkapiKy(); - const [namespace] = useNamespace('interfaces'); + const [namespace] = useNamespace({ key: 'interfaces' }); const { data,