{
diff --git a/src/components/common/VersionList.jsx b/src/components/common/VersionList.jsx
index 2d003977..d4ee276e 100644
--- a/src/components/common/VersionList.jsx
+++ b/src/components/common/VersionList.jsx
@@ -35,14 +35,15 @@ const VersionList = ({ versions, resource }) => {
setSelectedList(newSelectedList)
}
const isConcept = resource === 'concept';
- const canSelect = isConcept && sortedVersions.length > 1;
+ const isMapping = resource === 'mapping';
+ const canSelect = (isConcept || isMapping) && sortedVersions.length > 1;
const gridClass = canSelect ? 'col-md-11' : 'col-md-12'
- const showCompareOption = isConcept && selectedList.length === 2;
+ const showCompareOption = (isConcept || isMapping) && selectedList.length === 2;
const onCompareClick = event => {
event.stopPropagation()
event.preventDefault()
- const url = `#/concepts/compare?lhs=${selectedList[0]}&rhs=${selectedList[1]}`
+ const url = `#/${resource}s/compare?lhs=${selectedList[0]}&rhs=${selectedList[1]}`
window.open(url, '_blank')
}
diff --git a/src/components/concepts/ConceptsComparison.jsx b/src/components/concepts/ConceptsComparison.jsx
index dad33be2..e1c60068 100644
--- a/src/components/concepts/ConceptsComparison.jsx
+++ b/src/components/concepts/ConceptsComparison.jsx
@@ -1,28 +1,9 @@
-import React from 'react';
-import ReactDiffViewer from 'react-diff-viewer';
-import { Link } from 'react-router-dom';
-import {
- TableContainer, Table, TableHead, TableBody, TableCell, TableRow,
- CircularProgress, IconButton, Tooltip
-} from '@material-ui/core';
-import {
- ArrowDropDown as ArrowDownIcon, ArrowDropUp as ArrowUpIcon,
- Settings as SettingsIcon,
-} from '@material-ui/icons';
-import {
- get, startCase, map, isEmpty, includes, isEqual, size, filter, reject, keys, values,
- sortBy, findIndex, uniqBy, has, maxBy, cloneDeep, pickBy, forEach
-} from 'lodash';
+import React from 'react'
+import { cloneDeep, get, map, isEmpty, sortBy, filter, reject, uniqBy, findIndex, includes, has, keys, values } from 'lodash';
+import Comparison from '../common/Comparison'
import APIService from '../../services/APIService';
-import {
- formatDate, toObjectArray, toParentURI, sortObjectBy,
- memorySizeOf, formatByteSize
-} from '../../common/utils';
-import {
- DIFF_BG_RED,
-} from '../../common/constants';
-import ComparisonAttributes from './ComparisonAttributes';
-import ExtrasDiff from '../common/ExtrasDiff';
+import { toObjectArray, toParentURI, formatDate } from '../../common/utils';
+import { useLocation } from 'react-router-dom';
const getLocaleLabelExpanded = (locale, formatted=false) => {
if(!locale)
@@ -70,435 +51,171 @@ const getMappingLabel = (mapping, formatted=false) => {
return label
}
-
-class ConceptsComparison extends React.Component {
- constructor(props) {
- super(props);
- this.attributeState = {show: true, type: 'text'}
- this.state = {
- isVersion: false,
- isLoadingLHS: true,
- isLoadingRHS: true,
- lhs: {},
- rhs: {},
- drawer: false,
- attributes: {
- datatype: {...cloneDeep(this.attributeState), position: 1},
- display_locale: {...cloneDeep(this.attributeState), position: 2},
- external_id: {...cloneDeep(this.attributeState), position: 3},
- owner: {...cloneDeep(this.attributeState), type: 'textFormatted', position: 4},
- names: {...cloneDeep(this.attributeState), collapsed: true, type: 'list', position: 5},
- descriptions: {...cloneDeep(this.attributeState), collapsed: true, type: 'list', position: 6},
- parent_concept_urls: {...cloneDeep(this.attributeState), collapsed: true, type: 'list', position: 14},
- child_concept_urls: {...cloneDeep(this.attributeState), collapsed: true, type: 'list', position: 15},
- mappings: {...cloneDeep(this.attributeState), collapsed: true, type: 'list', position: 7},
- extras: {...cloneDeep(this.attributeState), collapsed: true, type: 'list', position: 8},
- retired: {...cloneDeep(this.attributeState), type: 'bool', position: 9},
- created_by: {...cloneDeep(this.attributeState), position: 10},
- updated_by: {...cloneDeep(this.attributeState), position: 11},
- created_on: {...cloneDeep(this.attributeState), type: 'date', position: 12},
- updated_on: {...cloneDeep(this.attributeState), type: 'date', position: 13},
- },
+export default function ConceptsComparison() {
+ const location = useLocation()
+ const attributeState = {show: true, type: 'text'}
+ const attributes = {
+ datatype: {...cloneDeep(attributeState), position: 1},
+ display_locale: {...cloneDeep(attributeState), position: 2},
+ external_id: {...cloneDeep(attributeState), position: 3},
+ owner: {...cloneDeep(attributeState), type: 'textFormatted', position: 4},
+ names: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 5},
+ descriptions: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 6},
+ parent_concept_urls: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 14},
+ child_concept_urls: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 15},
+ mappings: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 7},
+ extras: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 8},
+ retired: {...cloneDeep(attributeState), type: 'bool', position: 9},
+ created_by: {...cloneDeep(attributeState), position: 10},
+ updated_by: {...cloneDeep(attributeState), position: 11},
+ created_on: {...cloneDeep(attributeState), type: 'date', position: 12},
+ updated_on: {...cloneDeep(attributeState), type: 'date', position: 13},
}
- }
-
- componentDidMount() {
- this.setObjectsForComparison()
- }
-
- componentDidUpdate(prevProps) {
- if(prevProps.location.search !== this.props.location.search)
- this.setObjectsForComparison()
- }
-
- onDrawerClick = () => {
- this.setState({drawer: !this.state.drawer})
- }
-
- reorder = (startIndex, endIndex) => {
- const { attributes } = this.state;
- const attrs = keys(attributes);
- const result = Array.from(attrs);
- const [removed] = result.splice(startIndex, 1);
- result.splice(endIndex, 0, removed);
- const orderedAttrs = {};
- forEach(result, (attr, index) => {
- orderedAttrs[attr] = attributes[attr]
- orderedAttrs[attr].position = index + 1
- })
- return orderedAttrs;
- };
+ const fetcher = (uri, attr, loadingAttr, state) => {
+ if(uri && attr && loadingAttr) {
+ const { isVersion } = state;
+ const isAnyVersion = isVersion || uri.match(/\//g).length === 8;
+ return APIService
+ .new()
+ .overrideURL(encodeURI(uri))
+ .get(null, null, {includeInverseMappings: true, includeHierarchyPath: true, includeParentConceptURLs: true, includeChildConceptURLs: true})
+ .then(response => {
+ if(get(response, 'status') === 200) {
+ const newState = {...state}
+ newState[attr] = formatter(response.data)
+ newState[loadingAttr] = false
+ newState.isVersion = isAnyVersion
+ newState.attributes = attributes
+ if(isAnyVersion) {
+ newState.attributes['is_latest_version'] = {...cloneDeep(attributeState), type: 'bool', position: 14}
+ newState.attributes['update_comment'] = {...cloneDeep(attributeState), position: 15}
+ }
+ return newState
+ }
+ })
+ }
+ }
+ const formatter = (concept) => {
+ concept.names = sortLocales(concept.names)
+ concept.descriptions = sortLocales(concept.descriptions)
+ concept.originalExtras = concept.extras
+ concept.extras = toObjectArray(concept.extras)
+ return concept
+ }
- onAttributeDragEnd = result => {
- if(result.destination && result.source.index !== result.destination.index)
- this.setState({attributes: this.reorder(result.source.index, result.destination.index)})
- }
+ const sortLocales = locales => {
+ return sortBy([
+ ...filter(locales, {name_type: 'FULLY_SPECIFIED', locale_preferred: true}),
+ ...filter(reject(locales, {name_type: 'FULLY_SPECIFIED'}), {locale_preferred: true}),
+ ...filter(locales, {name_type: 'FULLY_SPECIFIED', locale_preferred: false}),
+ ...reject(reject(locales, {name_type: 'FULLY_SPECIFIED'}), {locale_preferred: true}),
+ ], 'locale')
+ }
- onToggleAttributeClick = attr => {
- this.setState({
- attributes: {
- ...this.state.attributes,
- [attr]: {
- ...this.state.attributes[attr],
- show: !this.state.attributes[attr].show
+ const sortMappings = (state) => {
+ if(!isEmpty(get(state.lhs, 'mappings')) && !isEmpty(get(state.rhs, 'mappings'))) {
+ const newState = {...state};
+ if(newState.lhs.mappings.length > newState.rhs.mappings.length) {
+ newState.lhs.mappings = uniqBy([...sortBy(
+ newState.rhs.mappings, m1 => findIndex(newState.lhs.mappings, m2 => m1.id === m2.id)
+ ), ...newState.lhs.mappings], 'id')
+ } else {
+ newState.rhs.mappings = uniqBy([...sortBy(
+ newState.lhs.mappings, m1 => findIndex(newState.rhs.mappings, m2 => m1.id === m2.id)
+ ), ...newState.rhs.mappings], 'id')
}
- }
- })
- }
- onCollapseIconClick(attr) {
- this.setState({
- attributes: {
- ...this.state.attributes,
- [attr]: {
- ...this.state.attributes[attr],
- collapsed: !this.state.attributes[attr].collapsed
- }
+ return newState
}
- })
- }
+ }
- setObjectsForComparison() {
- const queryParams = new URLSearchParams(this.props.location.search)
- this.fetchConcept(queryParams.get('lhs'), 'lhs', 'isLoadingLHS')
- this.fetchConcept(queryParams.get('rhs'), 'rhs', 'isLoadingRHS')
- }
+ const getHeaderSubAttributeValues = (concept, isVersion) => {
+ const attributes = [
+ {
+ name: "Source:",
+ value: concept.source,
+ url: toParentURI(concept.url)
+ },
+ {
+ name: "Type:",
+ value: concept.concept_class,
+ url: null
+ },
+ {
+ name: "UID:",
+ value: concept.id,
+ url: null
+ },
+ ]
+ if (isVersion) {
+ attributes.push({
+ name: "VERSION:",
+ value: concept.version,
+ url: null
+ })
+ }
- fetchConcept(uri, attr, loadingAttr) {
- if(uri && attr && loadingAttr) {
- const { isVersion } = this.state;
- const isAnyVersion = isVersion || uri.match(/\//g).length === 8;
- APIService
- .new()
- .overrideURL(encodeURI(uri))
- .get(null, null, {includeInverseMappings: true, includeHierarchyPath: true, includeParentConceptURLs: true, includeChildConceptURLs: true})
- .then(response => {
- if(get(response, 'status') === 200) {
- const newState = {...this.state}
- newState[attr] = this.formatConcept(response.data)
- newState[loadingAttr] = false
- newState.isVersion = isAnyVersion
- if(isAnyVersion) {
- newState.attributes['is_latest_version'] = {...cloneDeep(this.attributeState), type: 'bool', position: 14}
- newState.attributes['update_comment'] = {...cloneDeep(this.attributeState), position: 15}
- }
- this.setState(newState, this.sortMappings)
- }
- })
+ return attributes
}
- }
-
- formatConcept(concept) {
- concept.names = this.sortLocales(concept.names)
- concept.descriptions = this.sortLocales(concept.descriptions)
- concept.originalExtras = concept.extras
- concept.extras = toObjectArray(concept.extras)
- return concept
- }
- sortMappings() {
- if(!isEmpty(get(this.state.lhs, 'mappings')) && !isEmpty(get(this.state.rhs, 'mappings'))) {
- const newState = {...this.state};
- if(newState.lhs.mappings.length > newState.rhs.mappings.length) {
- newState.lhs.mappings = uniqBy([...sortBy(
- newState.rhs.mappings, m1 => findIndex(newState.lhs.mappings, m2 => m1.id === m2.id)
- ), ...newState.lhs.mappings], 'id')
+ const getAttributeValue = (concept, attr, type, formatted=false) => {
+ let value = get(concept, attr)
+ if (attr === 'extras')
+ return JSON.stringify(value, undefined, 2)
+ if(type === 'list') {
+ if(isEmpty(value)) return '';
+ if(includes(['names', 'descriptions'], attr))
+ return map(value, locale => getLocaleLabelExpanded(locale, formatted))
+ if (attr === 'mappings')
+ return map(value, mapping => getMappingLabel(mapping, formatted));
+ else
+ return value
+ } else if(type === 'date') {
+ if(attr === 'created_on')
+ value ||= get(concept, 'created_at')
+ if(attr === 'updated_on')
+ value ||= get(concept, 'updated_at')
+
+ return value ? formatDate(value) : '';
+ } else if (type === 'textFormatted') {
+ if(attr === 'owner')
+ return `${concept.owner_type}: ${concept.owner}`
+ } else if (type === 'bool') {
+ return value ? 'True' : 'False'
} else {
- newState.rhs.mappings = uniqBy([...sortBy(
- newState.lhs.mappings, m1 => findIndex(newState.rhs.mappings, m2 => m1.id === m2.id)
- ), ...newState.rhs.mappings], 'id')
+ if(includes(['created_by', 'updated_by'], attr))
+ value ||= get(concept, `version_${attr}`)
+ if(attr === 'updated_by' && has(concept, 'version_created_by'))
+ value ||= concept.version_created_by
+ return value || '';
}
-
- this.setState(newState)
}
- }
-
- sortLocales = locales => {
- return sortBy([
- ...filter(locales, {name_type: 'FULLY_SPECIFIED', locale_preferred: true}),
- ...filter(reject(locales, {name_type: 'FULLY_SPECIFIED'}), {locale_preferred: true}),
- ...filter(locales, {name_type: 'FULLY_SPECIFIED', locale_preferred: false}),
- ...reject(reject(locales, {name_type: 'FULLY_SPECIFIED'}), {locale_preferred: true}),
- ], 'locale')
- }
-
- getHeaderSubAttributes(concept) {
- return (
-
-
-
- Source:
-
- {concept.source}
-
-
-
- Type:
- {concept.concept_class}
-
-
- UID:
- {concept.id}
-
- {
- this.state.isVersion &&
-
- VERSION:
- {concept.version}
-
- }
-
-
- )
- }
- getValue(concept, attr, type, formatted=false) {
- let value = get(concept, attr)
- if (attr === 'extras')
- return JSON.stringify(value, undefined, 2)
- if(type === 'list') {
- if(isEmpty(value)) return '';
+ const getExtraAttributeLabel = (val) => {
+ if(!val)
+ return ''
+ return `${keys(val)[0]}: ${JSON.stringify(values(val)[0])}`
+ }
+
+ const getListAttributeValue = (attr, val, formatted=false) => {
if(includes(['names', 'descriptions'], attr))
- return map(value, locale => getLocaleLabelExpanded(locale, formatted))
- if (attr === 'mappings')
- return map(value, mapping => getMappingLabel(mapping, formatted));
- else
- return value
- } else if(type === 'date') {
- if(attr === 'created_on')
- value ||= get(concept, 'created_at')
- if(attr === 'updated_on')
- value ||= get(concept, 'updated_at')
-
- return value ? formatDate(value) : '';
- } else if (type === 'textFormatted') {
- if(attr === 'owner')
- return `${concept.owner_type}: ${concept.owner}`
- } else if (type === 'bool') {
- return value ? 'True' : 'False'
- } else {
- if(includes(['created_by', 'updated_by'], attr))
- value ||= get(concept, `version_${attr}`)
- if(attr === 'updated_by' && has(concept, 'version_created_by'))
- value ||= concept.version_created_by
- return value || '';
+ return getLocaleLabelExpanded(val, formatted)
+ if(includes(['mappings'], attr))
+ return getMappingLabel(val, formatted)
+ if(includes(['extras'], attr))
+ return getExtraAttributeLabel(val)
+ if(includes(['parent_concept_urls', 'child_concept_urls'], attr))
+ return val
}
- }
-
- maxArrayElement(v1, v2) {
- return maxBy([v1, v2], size)
- }
-
- getListAttrValue(attr, val, formatted=false) {
- if(includes(['names', 'descriptions'], attr))
- return getLocaleLabelExpanded(val, formatted)
- if(includes(['mappings'], attr))
- return getMappingLabel(val, formatted)
- if(includes(['extras'], attr))
- return this.getExtraAttributeLabel(val)
- if(includes(['parent_concept_urls', 'child_concept_urls'], attr))
- return val
- }
- getExtraAttributeLabel(val) {
- if(!val)
- return ''
- return `${keys(val)[0]}: ${JSON.stringify(values(val)[0])}`
- }
-
- getAttributeDOM(attr, type, lhsValue, rhsValue, isDiff) {
- const { lhs, rhs } = this.state;
- const maxLengthAttr = type === 'list' ? this.maxArrayElement(get(lhs, attr), get(rhs, attr)) : [];
- const rowSpan = size(maxLengthAttr);
- const isExtras = attr === 'extras';
- return (
-
- {
- isExtras ?
- :
- (
- type === 'list' ?
- map(maxLengthAttr, (_attr, index) => {
- const _lhsVal = get(lhs, `${attr}.${index}`, '')
- const _rhsVal = get(rhs, `${attr}.${index}`, '')
- const _lhsValCleaned = this.getListAttrValue(attr, _lhsVal)
- const _rhsValCleaned = this.getListAttrValue(attr, _rhsVal)
- const _isDiff = !isEqual(_lhsValCleaned, _rhsValCleaned);
- return (
-
- {
- index === 0 &&
-
- {type !== 'list' && startCase(attr)}
-
- }
- {
- _isDiff ?
-
-
- :
-
-
- {this.getListAttrValue(attr, _lhsVal, true)}
-
-
- {this.getListAttrValue(attr, _rhsVal, true)}
-
-
- }
-
- )
- }) :
-
-
- {startCase(attr)}
-
- {
- isDiff ?
-
-
- :
-
-
- {this.getValue(lhs, attr, type, true)}
-
-
- {this.getValue(rhs, attr, type, true)}
-
-
- }
-
- )
- }
-
- )
- }
-
- getHeaderCell = concept => {
- return (
-
-
- {this.getHeaderSubAttributes(concept)}
-
-
- {concept.display_name}
-
-
- )
- }
-
- render() {
- const { lhs, rhs, isLoadingLHS, isLoadingRHS, attributes, drawer } = this.state;
- const isLoading = isLoadingLHS || isLoadingRHS;
- const visibleAttributes = sortObjectBy(pickBy(attributes, {show: true}), config => config.position)
- return (
-
- {
- isLoading ?
-
-
-
:
-
-
-
-
-
-
-
-
-
-
-
-
- {
- map([lhs, rhs], this.getHeaderCell)
- }
-
-
-
- {
- map(visibleAttributes, (config, attr) => {
- const type = config.type;
- const lhsValue = this.getValue(lhs, attr, type);
- const rhsValue = this.getValue(rhs, attr, type);
- const isDiff = !isEqual(lhsValue, rhsValue);
- const children = this.getAttributeDOM(attr, type, lhsValue, rhsValue, isDiff);
- if(type === 'list') {
- const lhsRawValue = lhs[attr];
- const rhsRawValue = rhs[attr];
- const lhsCount = lhsRawValue.length;
- const rhsCount = rhsRawValue.length;
- const hasKids = Boolean(lhsCount || rhsCount);
- const styles = isDiff ? {background: DIFF_BG_RED} : {};
- const isExpanded = !config.collapsed || !hasKids;
- const isExtras = attr === 'extras';
- let lhsSize, rhsSize;
- let size = '';
- if(isExtras && (!isEmpty(lhsRawValue) || !isEmpty(rhsRawValue))) {
- lhsSize = memorySizeOf(lhsRawValue, false)
- rhsSize = memorySizeOf(rhsRawValue, false)
- const totalSize = lhsSize + rhsSize;
- const tooMany = totalSize/1024 >= 99; // More than 95KB
- size = `${formatByteSize(totalSize)}`;
- size = tooMany ? `${size} (this may take some time)` : size
- }
- return (
-
- this.onCollapseIconClick(attr)} style={{cursor: 'pointer'}}>
-
-
- {`${startCase(attr)} (${lhsCount}/${rhsCount})`}
- { size && {size} }
- {
- isExpanded ? :
- }
-
-
-
- {
- isExpanded &&
-
- {children}
-
- }
-
- )
- } else {
- return children;
- }
- })
- }
-
-
-
-
- }
-
-
- )
- }
+ return
}
-
-export default ConceptsComparison;
diff --git a/src/components/mappings/MappingsComparison.jsx b/src/components/mappings/MappingsComparison.jsx
new file mode 100644
index 00000000..b5eaadb5
--- /dev/null
+++ b/src/components/mappings/MappingsComparison.jsx
@@ -0,0 +1,130 @@
+import React from 'react'
+import Comparison from '../common/Comparison'
+import { cloneDeep, get, isEmpty, includes, has } from 'lodash'
+import { useLocation } from 'react-router-dom'
+import APIService from '../../services/APIService';
+import { formatDate, toObjectArray, toParentURI } from '../../common/utils';
+
+
+export default function MappingsComparison() {
+ const location = useLocation()
+ const attributeState = {show: true, type: 'text'}
+ const attributes = {
+ map_type: {...cloneDeep(attributeState), position: 1},
+ external_id: {...cloneDeep(attributeState), position: 2},
+ owner: {...cloneDeep(attributeState), type: 'textFormatted', position: 3},
+ from_source_owner: {...cloneDeep(attributeState), type: 'textFormatted', position: 4},
+ from_source_name: {...cloneDeep(attributeState), position: 5},
+ to_source_url: {...cloneDeep(attributeState), position: 6},
+ to_source_owner: {...cloneDeep(attributeState), type: 'textFormatted', position: 7},
+ to_source_name: {...cloneDeep(attributeState), position: 8},
+ from_concept_name: {...cloneDeep(attributeState), position: 9},
+ from_concept_url: {...cloneDeep(attributeState), position: 10},
+ to_concept_name: {...cloneDeep(attributeState), position: 11},
+ to_concept_url: {...cloneDeep(attributeState), position: 12},
+ extras: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 13},
+ retired: {...cloneDeep(attributeState), type: 'bool', position: 14},
+ created_by: {...cloneDeep(attributeState), position: 15},
+ updated_by: {...cloneDeep(attributeState), position: 16},
+ created_on: {...cloneDeep(attributeState), type: 'date', position: 17},
+ updated_on: {...cloneDeep(attributeState), type: 'date', position: 18},
+ }
+
+ const fetcher = (uri, attr, loadingAttr, state) => {
+ if(uri && attr && loadingAttr) {
+ const { isVersion } = state;
+ const isAnyVersion = isVersion || uri.match(/\//g).length === 8;
+ return APIService
+ .new()
+ .overrideURL(encodeURI(uri))
+ .get()
+ .then(response => {
+ if(get(response, 'status') === 200) {
+ const newState = {...state}
+ newState[attr] = formatter(response.data)
+ newState[loadingAttr] = false
+ newState.isVersion = isAnyVersion
+ newState.attributes = attributes
+ if(isAnyVersion) {
+ newState.attributes['is_latest_version'] = {...cloneDeep(attributeState), type: 'bool', position: 19}
+ newState.attributes['update_comment'] = {...cloneDeep(attributeState), position: 20}
+ }
+ return newState
+ }
+ })
+ }
+ }
+
+ const formatter = (mapping) => {
+ mapping.originalExtras = mapping.extras
+ mapping.extras = toObjectArray(mapping.extras)
+ return mapping
+ }
+
+ const getAttributeValue = (mapping, attr, type) => {
+ let value = get(mapping, attr)
+ if (attr === 'extras')
+ return JSON.stringify(value, undefined, 2)
+ if (attr === 'from_concept_name')
+ return value ||= get(mapping, 'from_concept_code')
+ if (attr === 'to_concept_name')
+ return value ||= get(mapping, 'to_concept_code')
+ if(type === 'list') {
+ if(isEmpty(value)) return '';
+ else return value
+ } else if(type === 'date') {
+ if(attr === 'created_on')
+ value ||= get(mapping, 'created_at')
+ if(attr === 'updated_on')
+ value ||= get(mapping, 'updated_at')
+
+ return value ? formatDate(value) : '';
+ } else if (type === 'textFormatted') {
+ if(attr === 'owner')
+ return `${mapping.owner_type}: ${mapping.owner}`
+ if(attr === 'to_source_owner')
+ return `${mapping.to_source_owner_type}: ${mapping.to_source_owner}`
+ if(attr === 'from_source_owner')
+ return `${mapping.from_source_owner_type}: ${mapping.from_source_owner}`
+ } else if (type === 'bool') {
+ return value ? 'True' : 'False'
+ } else {
+ if(includes(['created_by', 'updated_by'], attr))
+ value ||= get(mapping, `version_${attr}`)
+ if(attr === 'updated_by' && has(mapping, 'version_created_by'))
+ value ||= mapping.version_created_by
+ return value || '';
+ }
+ }
+
+ const getHeaderSubAttributeValues = (mapping, isVersion) => {
+ const attributes = [
+ {
+ name: "Source:",
+ value: mapping.source,
+ url: toParentURI(mapping.url)
+ },
+ {
+ name: "UID:",
+ value: mapping.id,
+ url: null
+ },
+ ]
+ if (isVersion) {
+ attributes.push({
+ name: "VERSION:",
+ value: mapping.version,
+ url: null
+ })
+ }
+
+ return attributes
+ }
+
+ return
+}
diff --git a/src/components/search/ResultConstants.js b/src/components/search/ResultConstants.js
index 04c2d2e9..2e541690 100644
--- a/src/components/search/ResultConstants.js
+++ b/src/components/search/ResultConstants.js
@@ -1,4 +1,4 @@
-import React from 'react';
+import React from "react";
import {
LocalOffer as LocalOfferIcon,
Link as LinkIcon,
@@ -7,237 +7,723 @@ import {
Person as PersonIcon,
Home as HomeIcon,
Loyalty as LoyaltyIcon,
-} from '@material-ui/icons'
-import { get, find, isEmpty, flatten, compact } from 'lodash';
+} from "@material-ui/icons";
+import { get, find, isEmpty, flatten, compact } from "lodash";
import {
- formatDate, formatWebsiteLink, formatDateTime
-} from '../../common/utils';
-import ReferenceChip from '../common/ReferenceChip';
-import OwnerChip from '../common/OwnerChip';
-import ToConceptLabelVertical from '../mappings/ToConceptLabelVertical';
-import FromConceptLabelVertical from '../mappings/FromConceptLabelVertical';
-import ConceptDisplayName from '../concepts/ConceptDisplayName';
+ formatDate,
+ formatWebsiteLink,
+ formatDateTime,
+} from "../../common/utils";
+import ReferenceChip from "../common/ReferenceChip";
+import OwnerChip from "../common/OwnerChip";
+import ToConceptLabelVertical from "../mappings/ToConceptLabelVertical";
+import FromConceptLabelVertical from "../mappings/FromConceptLabelVertical";
+import ConceptDisplayName from "../concepts/ConceptDisplayName";
export const ALL_COLUMNS = {
concepts: [
- {id: 'owner', label: 'Owner', value: 'owner', sortOn: 'owner', renderer: concept =>
, essential: false},
- {id: 'parent', label: 'Source', value: 'source', sortOn: 'source', essential: false},
- {id: 'id', label: 'ID', value: 'id', sortOn: 'id', className: 'small'},
- {id: 'name', label: 'Display Name', value: 'display_name', sortOn: '_name', renderer: concept => (
), className: 'medium', sortBy: 'asc', tooltip: 'The display name is the preferred name for a source’s default locale.'},
- {id: 'class', label: 'Class', value: 'concept_class', sortOn: 'concept_class'},
- {id: 'datatype', label: 'Datatype', value: 'datatype', sortOn: 'datatype'},
- {id: 'updatedOn', label: 'UpdatedOn', value: 'version_created_on', formatter: formatDate, sortOn: 'last_update'},
+ {
+ id: "owner",
+ label: "Owner",
+ value: "owner",
+ sortOn: "owner",
+ renderer: (concept) => (
+
+ ),
+ essential: false,
+ },
+ {
+ id: "parent",
+ label: "Source",
+ value: "source",
+ sortOn: "source",
+ essential: false,
+ },
+ { id: "id", label: "ID", value: "id", sortOn: "id", className: "small" },
+ {
+ id: "name",
+ label: "Display Name",
+ value: "display_name",
+ sortOn: "_name",
+ renderer: (concept) =>
,
+ className: "medium",
+ sortBy: "asc",
+ tooltip:
+ "The display name is the preferred name for a source’s default locale.",
+ },
+ {
+ id: "class",
+ label: "Class",
+ value: "concept_class",
+ sortOn: "concept_class",
+ },
+ {
+ id: "datatype",
+ label: "Datatype",
+ value: "datatype",
+ sortOn: "datatype",
+ },
+ {
+ id: "updatedOn",
+ label: "UpdatedOn",
+ value: "version_created_on",
+ formatter: formatDate,
+ sortOn: "last_update",
+ },
],
mappings: [
- {id: 'owner', label: 'Owner', value: 'owner', sortOn: 'owner', renderer: mapping =>
, essential: false},
- {id: 'parent', label: 'Source', value: 'source', sortOn: 'source', essential: false, className: 'xsmall'},
- {id: 'id', label: 'ID', value: 'id', sortOn: 'id', className: 'small'},
- {id: 'from', label: 'From Concept', renderer: mapping =>
, className: 'medium'},
- {id: 'mapType', label: 'Type', value: 'map_type', sortOn: 'map_type', className: 'xxsmall'},
- {id: 'to', label: 'To Concept', renderer: mapping =>
, className: 'medium'},
- {id: 'updatedOn', label: 'UpdatedOn', value: 'version_created_on', formatter: formatDate, sortOn: 'last_update', className: 'xxsmall'},
+ {
+ id: "owner",
+ label: "Owner",
+ value: "owner",
+ sortOn: "owner",
+ renderer: (mapping) => (
+
+ ),
+ essential: false,
+ },
+ {
+ id: "parent",
+ label: "Source",
+ value: "source",
+ sortOn: "source",
+ essential: false,
+ className: "xsmall",
+ },
+ { id: "id", label: "ID", value: "id", sortOn: "id", className: "small" },
+ {
+ id: "from",
+ label: "From Concept",
+ renderer: (mapping) => (
+
+ ),
+ className: "medium",
+ },
+ {
+ id: "mapType",
+ label: "Type",
+ value: "map_type",
+ sortOn: "map_type",
+ className: "xxsmall",
+ },
+ {
+ id: "to",
+ label: "To Concept",
+ renderer: (mapping) =>
,
+ className: "medium",
+ },
+ {
+ id: "updatedOn",
+ label: "UpdatedOn",
+ value: "version_created_on",
+ formatter: formatDate,
+ sortOn: "last_update",
+ className: "xxsmall",
+ },
],
sources: [
- {id: 'owner', label: 'Owner', value: 'owner', sortOn: 'owner', renderer: source =>
, essential: false},
- {id: 'id', label: 'ID', value: 'short_code', sortOn: 'mnemonic'},
- {id: 'name', label: 'Name', value: 'name', sortOn: 'name', sortBy: 'asc'},
- {id: 'sourceType', label: 'Type', value: 'source_type', sortOn: 'source_type'},
- {id: 'uuid', label: 'UUID', value: 'uuid', sortable: false},
- {id: 'full_name', label: 'Full Name', value: 'full_name', sortOn: 'full_name', sortBy: 'asc'},
- {id: 'description', label: 'Description', value: 'description', sortable: false},
- {id: 'public_access', label: 'Public Access', value: 'public_access', sortable: false},
- {id: 'default_locale', label: 'Default Locale', value: 'default_locale', sortable: false},
- {id: 'website', label: 'Website', value: 'website', sortable: false, formatter: formatWebsiteLink},
- {id: 'external_id', label: 'External ID', value: 'external_id', sortable: false},
- {id: 'canonical_url', label: 'Canonical URL', value: 'canonical_url', sortable: false, formatter: formatWebsiteLink},
- {id: 'publisher', label: 'Publisher', value: 'publisher', sortable: false},
- {id: 'purpose', label: 'Purpose', value: 'purpose', sortable: false},
- {id: 'copyright', label: 'Copyright', value: 'copyright', sortable: false},
- {id: 'content_type', label: 'Content Type', value: 'content_type', sortable: false},
- {id: 'revision_date', label: 'Revision Date', value: 'revision_date', sortable: false, formatter: formatDate},
+ {
+ id: "owner",
+ label: "Owner",
+ value: "owner",
+ sortOn: "owner",
+ renderer: (source) => (
+
+ ),
+ essential: false,
+ },
+ { id: "id", label: "ID", value: "short_code", sortOn: "mnemonic" },
+ { id: "name", label: "Name", value: "name", sortOn: "name", sortBy: "asc" },
+ {
+ id: "sourceType",
+ label: "Type",
+ value: "source_type",
+ sortOn: "source_type",
+ },
+ { id: "uuid", label: "UUID", value: "uuid", sortable: false },
+ {
+ id: "full_name",
+ label: "Full Name",
+ value: "full_name",
+ sortOn: "full_name",
+ sortBy: "asc",
+ },
+ {
+ id: "description",
+ label: "Description",
+ value: "description",
+ sortable: false,
+ },
+ {
+ id: "public_access",
+ label: "Public Access",
+ value: "public_access",
+ sortable: false,
+ },
+ {
+ id: "default_locale",
+ label: "Default Locale",
+ value: "default_locale",
+ sortable: false,
+ },
+ {
+ id: "website",
+ label: "Website",
+ value: "website",
+ sortable: false,
+ formatter: formatWebsiteLink,
+ },
+ {
+ id: "external_id",
+ label: "External ID",
+ value: "external_id",
+ sortable: false,
+ },
+ {
+ id: "canonical_url",
+ label: "Canonical URL",
+ value: "canonical_url",
+ sortable: false,
+ formatter: formatWebsiteLink,
+ },
+ {
+ id: "publisher",
+ label: "Publisher",
+ value: "publisher",
+ sortable: false,
+ },
+ { id: "purpose", label: "Purpose", value: "purpose", sortable: false },
+ {
+ id: "copyright",
+ label: "Copyright",
+ value: "copyright",
+ sortable: false,
+ },
+ {
+ id: "content_type",
+ label: "Content Type",
+ value: "content_type",
+ sortable: false,
+ },
+ {
+ id: "revision_date",
+ label: "Revision Date",
+ value: "revision_date",
+ sortable: false,
+ formatter: formatDate,
+ },
],
collections: [
- {id: 'owner', label: 'Owner', value: 'owner', sortOn: 'owner', renderer: coll =>
, essential: false},
- {id: 'id', label: 'ID', value: 'short_code', sortOn: 'mnemonic'},
- {id: 'name', label: 'Name', value: 'name', sortOn: 'name', sortBy: 'asc'},
- {id: 'collectionType', label: 'Type', value: 'collection_type', sortOn: 'collection_type'},
- {id: 'full_name', label: 'Full Name', value: 'full_name', sortOn: 'full_name', sortBy: 'asc'},
- {id: 'uuid', label: 'UUID', value: 'uuid', sortable: false},
- {id: 'description', label: 'Description', value: 'description', sortable: false},
- {id: 'public_access', label: 'Public Access', value: 'public_access', sortable: false},
- {id: 'default_locale', label: 'Default Locale', value: 'default_locale', sortable: false},
- {id: 'website', label: 'Website', value: 'website', sortable: false, formatter: formatWebsiteLink},
- {id: 'external_id', label: 'External ID', value: 'external_id', sortable: false},
- {id: 'canonical_url', label: 'Canonical URL', value: 'canonical_url', sortable: false, formatter: formatWebsiteLink},
- {id: 'publisher', label: 'Publisher', value: 'publisher', sortable: false},
- {id: 'purpose', label: 'Purpose', value: 'purpose', sortable: false},
- {id: 'copyright', label: 'Copyright', value: 'copyright', sortable: false},
- {id: 'revision_date', label: 'Revision Date', value: 'revision_date', sortable: false, formatter: formatDate},
+ {
+ id: "owner",
+ label: "Owner",
+ value: "owner",
+ sortOn: "owner",
+ renderer: (coll) => (
+
+ ),
+ essential: false,
+ },
+ { id: "id", label: "ID", value: "short_code", sortOn: "mnemonic" },
+ { id: "name", label: "Name", value: "name", sortOn: "name", sortBy: "asc" },
+ {
+ id: "collectionType",
+ label: "Type",
+ value: "collection_type",
+ sortOn: "collection_type",
+ },
+ {
+ id: "full_name",
+ label: "Full Name",
+ value: "full_name",
+ sortOn: "full_name",
+ sortBy: "asc",
+ },
+ { id: "uuid", label: "UUID", value: "uuid", sortable: false },
+ {
+ id: "description",
+ label: "Description",
+ value: "description",
+ sortable: false,
+ },
+ {
+ id: "public_access",
+ label: "Public Access",
+ value: "public_access",
+ sortable: false,
+ },
+ {
+ id: "default_locale",
+ label: "Default Locale",
+ value: "default_locale",
+ sortable: false,
+ },
+ {
+ id: "website",
+ label: "Website",
+ value: "website",
+ sortable: false,
+ formatter: formatWebsiteLink,
+ },
+ {
+ id: "external_id",
+ label: "External ID",
+ value: "external_id",
+ sortable: false,
+ },
+ {
+ id: "canonical_url",
+ label: "Canonical URL",
+ value: "canonical_url",
+ sortable: false,
+ formatter: formatWebsiteLink,
+ },
+ {
+ id: "publisher",
+ label: "Publisher",
+ value: "publisher",
+ sortable: false,
+ },
+ { id: "purpose", label: "Purpose", value: "purpose", sortable: false },
+ {
+ id: "copyright",
+ label: "Copyright",
+ value: "copyright",
+ sortable: false,
+ },
+ {
+ id: "revision_date",
+ label: "Revision Date",
+ value: "revision_date",
+ sortable: false,
+ formatter: formatDate,
+ },
],
organizations: [
- {id: 'id', label: 'ID', value: 'id', sortOn: 'mnemonic', renderer: org =>
},
- {id: 'name', label: 'Name', value: 'name', sortOn: 'name', sortBy: 'asc'},
- {id: 'createdOn', label: 'Created On', value: 'created_on', formatter: formatDate, sortOn: 'created_on'},
+ {
+ id: "id",
+ label: "ID",
+ value: "id",
+ sortOn: "mnemonic",
+ renderer: (org) =>
,
+ },
+ { id: "name", label: "Name", value: "name", sortOn: "name", sortBy: "asc" },
+ {
+ id: "createdOn",
+ label: "Created On",
+ value: "created_on",
+ formatter: formatDate,
+ sortOn: "created_on",
+ },
],
users: [
- {id: 'username', label: 'Username', value: 'username', sortOn: 'username', renderer: user =>
, sortBy: 'asc'},
- {id: 'name', label: 'Name', value: 'name', sortOn: 'name', sortBy: 'asc'},
- {id: 'date_joined', label: 'Joined On', value: 'date_joined', formatter: formatDate, sortOn: 'date_joined'},
- {id: 'email', label: 'Email', value: 'email', sortable: false},
- {id: 'company', label: 'Company', value: 'company'},
- {id: 'location', label: 'Location', value: 'location'},
- {id: 'website', label: 'Website', value: 'website', sortable: false, formatter: formatWebsiteLink},
- {id: 'last_login', label: 'Last Login', value: 'last_login', sortable: false, formatter: formatDateTime},
+ {
+ id: "username",
+ label: "Username",
+ value: "username",
+ sortOn: "username",
+ renderer: (user) =>
,
+ sortBy: "asc",
+ },
+ { id: "name", label: "Name", value: "name", sortOn: "name", sortBy: "asc" },
+ {
+ id: "date_joined",
+ label: "Joined On",
+ value: "date_joined",
+ formatter: formatDate,
+ sortOn: "date_joined",
+ },
+ { id: "email", label: "Email", value: "email", sortable: false },
+ { id: "company", label: "Company", value: "company" },
+ { id: "location", label: "Location", value: "location" },
+ {
+ id: "website",
+ label: "Website",
+ value: "website",
+ sortable: false,
+ formatter: formatWebsiteLink,
+ },
+ {
+ id: "last_login",
+ label: "Last Login",
+ value: "last_login",
+ sortable: false,
+ formatter: formatDateTime,
+ },
],
references: [
- {id: 'expression', label: 'Reference', value: 'expression', sortable: false, renderer: reference =>
},
+ {
+ id: "expression",
+ label: "Reference",
+ value: "expression",
+ sortable: false,
+ renderer: (reference) =>
,
+ },
],
CodeSystem: [
- {id: '_id', label: 'ID', value: 'resource.id', sortOn: '_id', sortBy: 'asc'},
- {id: 'url', label: 'Canonical URL', value: 'resource.url', sortable: false},
- {id: 'name', label: 'Name', value: 'resource.name', renderer: codeSystem =>
{codeSystem.resource.name}, sortOn: 'name', sortBy: 'asc'},
- {id: 'version', label: 'Latest Version', value: 'resource.version', sortOn: 'version', sortBy: 'asc'},
- {id: 'status', label: 'Status', value: 'resource.status', sortOn: 'status', sortBy: 'asc'},
- {id: 'content', label: 'Content', value: 'resource.content', sortOn: 'content', sortBy: 'asc'},
- {id: 'date', label: 'Release Date', value: 'resource.date', sortOn: 'date', formatter: formatDate},
- {id: 'publisher', label: 'Publisher', value: 'resource.publisher', sortOn: 'publisher', sortBy: 'asc'},
+ {
+ id: "_id",
+ label: "ID",
+ value: "resource.id",
+ sortOn: "_id",
+ sortBy: "asc",
+ },
+ {
+ id: "url",
+ label: "Canonical URL",
+ value: "resource.url",
+ sortable: false,
+ },
+ {
+ id: "name",
+ label: "Name",
+ value: "resource.name",
+ renderer: (codeSystem) => (
+
+ {codeSystem.resource.name}
+
+ ),
+ sortOn: "name",
+ sortBy: "asc",
+ },
+ {
+ id: "version",
+ label: "Latest Version",
+ value: "resource.version",
+ sortOn: "version",
+ sortBy: "asc",
+ },
+ {
+ id: "status",
+ label: "Status",
+ value: "resource.status",
+ sortOn: "status",
+ sortBy: "asc",
+ },
+ {
+ id: "content",
+ label: "Content",
+ value: "resource.content",
+ sortOn: "content",
+ sortBy: "asc",
+ },
+ {
+ id: "date",
+ label: "Release Date",
+ value: "resource.date",
+ sortOn: "date",
+ formatter: formatDate,
+ },
+ {
+ id: "publisher",
+ label: "Publisher",
+ value: "resource.publisher",
+ sortOn: "publisher",
+ sortBy: "asc",
+ },
],
ValueSet: [
- {id: '_id', label: 'ID', value: 'resource.id', sortOn: '_id', sortBy: 'asc'},
- {id: 'url', label: 'Canonical URL', value: 'resource.url', sortable: false},
- {id: 'name', label: 'Name', value: 'resource.name', renderer: codeSystem =>
{codeSystem.resource.name}, sortOn: 'name', sortBy: 'asc'},
- {id: 'version', label: 'Latest Version', value: 'resource.version', sortOn: 'version', sortBy: 'asc'},
- {id: 'status', label: 'Status', value: 'resource.status', sortOn: 'status', sortBy: 'asc'},
- {id: 'date', label: 'Release Date', value: 'resource.date', sortOn: 'date', formatter: formatDate},
- {id: 'publisher', label: 'Publisher', value: 'resource.publisher', sortOn: 'publisher', sortBy: 'asc'},
+ {
+ id: "_id",
+ label: "ID",
+ value: "resource.id",
+ sortOn: "_id",
+ sortBy: "asc",
+ },
+ {
+ id: "url",
+ label: "Canonical URL",
+ value: "resource.url",
+ sortable: false,
+ },
+ {
+ id: "name",
+ label: "Name",
+ value: "resource.name",
+ renderer: (codeSystem) => (
+
+ {codeSystem.resource.name}
+
+ ),
+ sortOn: "name",
+ sortBy: "asc",
+ },
+ {
+ id: "version",
+ label: "Latest Version",
+ value: "resource.version",
+ sortOn: "version",
+ sortBy: "asc",
+ },
+ {
+ id: "status",
+ label: "Status",
+ value: "resource.status",
+ sortOn: "status",
+ sortBy: "asc",
+ },
+ {
+ id: "date",
+ label: "Release Date",
+ value: "resource.date",
+ sortOn: "date",
+ formatter: formatDate,
+ },
+ {
+ id: "publisher",
+ label: "Publisher",
+ value: "resource.publisher",
+ sortOn: "publisher",
+ sortBy: "asc",
+ },
],
ConceptMap: [
- {id: '_id', label: 'ID', value: 'resource.id', sortOn: '_id', sortBy: 'asc'},
- {id: 'url', label: 'Canonical URL', value: 'resource.url', sortable: false},
- {id: 'name', label: 'Name', value: 'resource.title', renderer: codeSystem =>
{codeSystem.resource.title}, sortOn: 'title', sortBy: 'asc'},
- {id: 'version', label: 'Latest Version', value: 'resource.version', sortOn: 'version', sortBy: 'asc'},
- {id: 'status', label: 'Status', value: 'resource.status', sortOn: 'status', sortBy: 'asc'},
- {id: 'date', label: 'Release Date', value: 'resource.date', sortOn: 'date', formatter: formatDate},
- {id: 'publisher', label: 'Publisher', value: 'resource.publisher', sortOn: 'publisher', sortBy: 'asc'},
- ]
+ {
+ id: "_id",
+ label: "ID",
+ value: "resource.id",
+ sortOn: "_id",
+ sortBy: "asc",
+ },
+ {
+ id: "url",
+ label: "Canonical URL",
+ value: "resource.url",
+ sortable: false,
+ },
+ {
+ id: "name",
+ label: "Name",
+ value: "resource.title",
+ renderer: (codeSystem) => (
+
+ {codeSystem.resource.title}
+
+ ),
+ sortOn: "title",
+ sortBy: "asc",
+ },
+ {
+ id: "version",
+ label: "Latest Version",
+ value: "resource.version",
+ sortOn: "version",
+ sortBy: "asc",
+ },
+ {
+ id: "status",
+ label: "Status",
+ value: "resource.status",
+ sortOn: "status",
+ sortBy: "asc",
+ },
+ {
+ id: "date",
+ label: "Release Date",
+ value: "resource.date",
+ sortOn: "date",
+ formatter: formatDate,
+ },
+ {
+ id: "publisher",
+ label: "Publisher",
+ value: "resource.publisher",
+ sortOn: "publisher",
+ sortBy: "asc",
+ },
+ ],
};
-const TAG_ICON_STYLES = {width: '12px', marginRight: '2px', marginTop: '2px'}
+const TAG_ICON_STYLES = { width: "12px", marginRight: "2px", marginTop: "2px" };
export const CONCEPT_CONTAINER_RESOURCE_CHILDREN_TAGS = [
{
- id: 'activeConcepts',
- value: 'summary.active_concepts',
- label: 'Concepts',
- icon:
,
- hrefAttr: 'concepts_url'
+ id: "activeConcepts",
+ value: "summary.active_concepts",
+ label: "Concepts",
+ icon:
,
+ hrefAttr: "concepts_url",
},
{
- id: 'activeMappings',
- value: 'summary.active_mappings',
- label: 'Mappings',
- icon:
,
- hrefAttr: 'mappings_url'
+ id: "activeMappings",
+ value: "summary.active_mappings",
+ label: "Mappings",
+ icon:
,
+ hrefAttr: "mappings_url",
},
-]
+];
const CONCEPT_CONTAINER_TAGS = [
...CONCEPT_CONTAINER_RESOURCE_CHILDREN_TAGS,
{
- id: 'versions',
- value: 'summary.versions',
- label: 'Versions',
- icon:
,
- hrefAttr: 'versions_url'
+ id: "versions",
+ value: "summary.versions",
+ label: "Versions",
+ icon:
,
+ hrefAttr: "versions_url",
},
-]
+];
-const getOCLFHIRResourceURL = item => {
- const identifiers = flatten([get(item, 'resource.identifier', [])])
- return '/fhir/' + compact(get(find(identifiers, ident => get(ident, 'system', '').match('fhir.')), 'value', '').split('/')).splice(0, 4).join('/')
-}
+const getOCLFHIRResourceURL = (item) => {
+ const identifiers = flatten([get(item, "resource.identifier", [])]);
+ return (
+ "/fhir/" +
+ compact(
+ get(
+ find(identifiers, (ident) => get(ident, "system", "").match("fhir.")),
+ "value",
+ ""
+ ).split("/")
+ )
+ .splice(0, 4)
+ .join("/")
+ );
+};
const CODE_SYSTEM_TAGS = [
{
- id: 'count',
- getValue: item => (get(item, 'resource.count') || (get(item, 'resource.concept', []) || []).length).toLocaleString(),
- label: 'Concepts',
- icon:
,
- hrefAttr: (item, hapi) => hapi ? `/fhir/CodeSystem/${item.resource.id}/` : getOCLFHIRResourceURL(item)
+ id: "count",
+ getValue: (item) =>
+ (
+ get(item, "resource.count") ||
+ (get(item, "resource.concept", []) || []).length
+ ).toLocaleString(),
+ label: "Concepts",
+ icon:
,
+ hrefAttr: (item, hapi) =>
+ hapi
+ ? `/fhir/CodeSystem/${item.resource.id}/`
+ : getOCLFHIRResourceURL(item),
},
-]
+];
const VALUE_SET_TAGS = [
{
- id: 'count',
- getValue: item => {
- const concepts = get(item, 'resource.count') ||
- get(find(get(item, 'resource.compose.include', []), inc => !isEmpty(get(inc, 'concept'))), 'concept', []).length ||
- 0;
- return concepts.toLocaleString()
- },
- label: 'Concepts',
- icon:
,
- hrefAttr: (item, hapi) => hapi ? `/fhir/ValueSet/${item.resource.id}/` : getOCLFHIRResourceURL(item)
+ id: "count",
+ getValue: (item) => {
+ const concepts =
+ get(item, "resource.count") ||
+ get(
+ find(
+ get(item, "resource.compose.include", []),
+ (inc) => !isEmpty(get(inc, "concept"))
+ ),
+ "concept",
+ []
+ ).length ||
+ 0;
+ return concepts.toLocaleString();
+ },
+ label: "Concepts",
+ icon:
,
+ hrefAttr: (item, hapi) =>
+ hapi
+ ? `/fhir/ValueSet/${item.resource.id}/`
+ : getOCLFHIRResourceURL(item),
},
-]
+];
-export const CODE_SYSTEM_VERSION_TAGS = [...CODE_SYSTEM_TAGS]
-export const VALUE_SET_VERSION_TAGS = [...VALUE_SET_TAGS]
+export const CODE_SYSTEM_VERSION_TAGS = [...CODE_SYSTEM_TAGS];
+export const VALUE_SET_VERSION_TAGS = [...VALUE_SET_TAGS];
const SOURCE_TAG = {
- id: 'sources',
- value: 'public_sources',
- label: 'Public Sources',
- icon:
,
- hrefAttr: 'sources_url'
-}
+ id: "sources",
+ value: "public_sources",
+ label: "Public Sources",
+ icon:
,
+ hrefAttr: "sources_url",
+};
const COLLECTION_TAG = {
- id: 'collections',
- value: 'public_collections',
- label: 'Public Collections',
- icon:
,
- hrefAttr: 'collections_url'
-}
+ id: "collections",
+ value: "public_collections",
+ label: "Public Collections",
+ icon:
,
+ hrefAttr: "collections_url",
+};
export const TAGS = {
sources: [...CONCEPT_CONTAINER_TAGS],
collections: [...CONCEPT_CONTAINER_TAGS],
organizations: [
{
- id: 'members',
- value: 'members',
- label: 'Members',
- icon:
,
- hrefAttr: 'url'
+ id: "members",
+ value: "members",
+ label: "Members",
+ icon:
,
+ hrefAttr: "url",
},
SOURCE_TAG,
COLLECTION_TAG,
],
users: [
{
- id: 'orgs',
- value: 'orgs',
- label: 'Organizations',
- icon:
,
- hrefAttr: 'organizations_url'
+ id: "orgs",
+ value: "orgs",
+ label: "Organizations",
+ icon:
,
+ hrefAttr: "organizations_url",
},
SOURCE_TAG,
COLLECTION_TAG,
],
CodeSystem: [...CODE_SYSTEM_TAGS],
- ValueSet: [...VALUE_SET_TAGS]
-}
+ ValueSet: [...VALUE_SET_TAGS],
+};
export const FACET_ORDER = {
- concepts: ['owner', 'ownerType', 'source', 'conceptClass', 'datatype', 'locale', 'retired', 'collection_membership'],
+ concepts: [
+ "owner",
+ "ownerType",
+ "source",
+ "conceptClass",
+ "datatype",
+ "locale",
+ "retired",
+ "collection_membership",
+ ],
mappings: [
- 'owner', 'ownerType', 'source', 'mapType',
- 'fromConceptOwner', 'fromConceptOwnerType', 'fromConceptSource', 'fromConcept',
- 'toConceptOwner', 'toConceptOwnerType', 'toConceptSource', 'toConcept',
- 'retired', 'collection_membership',
- ]
-}
+ "owner",
+ "ownerType",
+ "source",
+ "mapType",
+ "fromConceptOwner",
+ "fromConceptOwnerType",
+ "fromConceptSource",
+ "fromConcept",
+ "toConceptOwner",
+ "toConceptOwnerType",
+ "toConceptSource",
+ "toConcept",
+ "retired",
+ "collection_membership",
+ ],
+};
export const SORT_ATTRS = {
- concepts: ['score', 'last_update', 'id', 'numeric_id', '_name', 'concept_class', 'datatype', 'source', 'owner'],
- mappings: ['score', 'last_update', 'id', 'map_type', 'source', 'owner'],
- users: ['score', 'username', 'date_joined', 'company', 'location'],
- organizations: ['score', 'last_update', 'name', 'mnemonic'],
- sources: ['score', 'last_update', 'mnemonic', 'source_type', 'name', 'owner', 'canonical_url'],
- collections: ['score', 'last_update', 'mnemonic', 'collection_type', 'name', 'owner', 'canonical_url'],
-}
+ concepts: [
+ "score",
+ "last_update",
+ "id",
+ "numeric_id",
+ "_name",
+ "concept_class",
+ "datatype",
+ "source",
+ "owner",
+ ],
+ mappings: ["score", "last_update", "id", "map_type", "source", "owner"],
+ users: ["score", "username", "date_joined", "company", "location"],
+ organizations: ["score", "last_update", "name", "mnemonic"],
+ sources: [
+ "score",
+ "last_update",
+ "mnemonic",
+ "source_type",
+ "name",
+ "owner",
+ "canonical_url",
+ ],
+ collections: [
+ "score",
+ "last_update",
+ "mnemonic",
+ "collection_type",
+ "name",
+ "owner",
+ "canonical_url",
+ ],
+};
diff --git a/src/components/search/ResultsTable.jsx b/src/components/search/ResultsTable.jsx
index 924ec92e..b900fe73 100644
--- a/src/components/search/ResultsTable.jsx
+++ b/src/components/search/ResultsTable.jsx
@@ -622,7 +622,7 @@ const ExpandibleRow = props => {
))
}
{
- !isSelectable &&
+ !isSelectable || true &&
{
resourceDefinition.tagWaitAttribute && !has(item, resourceDefinition.tagWaitAttribute) ?
@@ -770,7 +770,7 @@ const ResultsTable = (
const [orderBy, setOrderBy] = React.useState(defaultOrderBy)
const [order, setOrder] = React.useState(defaultOrder)
const hasAccess = currentUserHasAccess()
- const isSourceChild = includes(['concepts', 'mappings'], resource);
+ const isSourceChild = includes(['concepts', 'mappings', 'sources', 'collections'], resource);
const isReferenceResource = resource === 'references';
const isSelectable = (isReferenceResource && hasAccess && isVersionedObject) ||
isSourceChild;
@@ -896,7 +896,7 @@ const ResultsTable = (
})
}
{
- !isSelectable &&
+ !isSelectable || true &&
}
{
@@ -952,4 +952,4 @@ const ResultsTable = (
)
}
-export default ResultsTable
+export default ResultsTable
\ No newline at end of file
diff --git a/src/components/search/SelectedResourceControls.jsx b/src/components/search/SelectedResourceControls.jsx
index a8616714..aea41582 100644
--- a/src/components/search/SelectedResourceControls.jsx
+++ b/src/components/search/SelectedResourceControls.jsx
@@ -20,9 +20,10 @@ const SelectedResourceControls = ({
const isReferenceResource = resource === 'references';
const isConceptResource = resource === 'concepts';
const isSourceChild = includes(['concepts', 'mappings'], resource);
+ const isComparable = includes(['concepts', 'mappings', 'sources', 'collections'], resource);
const hasSelectedItems = selectedItems.length > 0;
const shouldShowDownloadOption = isSourceChild && hasSelectedItems;
- const shouldShowCompareOption = isConceptResource && selectedItems.length === 2;
+ const shouldShowCompareOption = isComparable && selectedItems.length === 2;
const shouldShowCreateSimilarOption = isSourceChild && hasAccess && selectedItems.length == 1 && onCreateSimilarClick;
const shouldShowAddToCollection = isSourceChild && isAuthenticated && hasSelectedItems;
const shouldShowCreateMappingOption = isConceptResource && hasAccess && hasSelectedItems && selectedItems.length <= 2 && onCreateMappingClick;
@@ -41,7 +42,7 @@ const SelectedResourceControls = ({
event.preventDefault()
const urls = map(selectedItems, 'url')
if(urls.length == 2) {
- const url = `#/concepts/compare?lhs=${urls[0]}&rhs=${urls[1]}`
+ const url = `#/${resource}/compare?lhs=${urls[0]}&rhs=${urls[1]}`
window.open(url, '_blank')
}
}
diff --git a/src/components/sources/SourceComparison.jsx b/src/components/sources/SourceComparison.jsx
new file mode 100644
index 00000000..da45a374
--- /dev/null
+++ b/src/components/sources/SourceComparison.jsx
@@ -0,0 +1,153 @@
+import React from 'react'
+import Comparison from '../common/Comparison'
+import { cloneDeep, get, isEmpty, includes, has } from 'lodash'
+import { useLocation } from 'react-router-dom'
+import APIService from '../../services/APIService';
+import { formatDate, toObjectArray, toParentURI } from '../../common/utils';
+
+
+export default function SourceComparison() {
+ const location = useLocation()
+ const attributeState = {show: true, type: 'text'}
+ const attributes = {
+ description: {...cloneDeep(attributeState), position: 1},
+ source_type: {...cloneDeep(attributeState), position: 2},
+ custom_validation_schema: {...cloneDeep(attributeState), position: 3},
+ public_access: {...cloneDeep(attributeState), position: 4},
+ default_locale: {...cloneDeep(attributeState), position: 5},
+ website: {...cloneDeep(attributeState), type:"url", position: 6},
+ custom_resources_linked_source: {...cloneDeep(attributeState), position: 7},
+ repository_type: {...cloneDeep(attributeState), position: 8},
+ preferred_source: {...cloneDeep(attributeState), position: 9},
+ canonical_url: {...cloneDeep(attributeState), type:"url", position: 10},
+ publisher: {...cloneDeep(attributeState), position: 11},
+ purpose: {...cloneDeep(attributeState), position: 12},
+ copyright: {...cloneDeep(attributeState), position: 13},
+ meta: {...cloneDeep(attributeState), position: 14},
+ immutable: {...cloneDeep(attributeState), position: 15},
+ revision_date: {...cloneDeep(attributeState), type:"url", position: 16},
+ logo_url: {...cloneDeep(attributeState), type:"url", position: 17},
+ text: {...cloneDeep(attributeState), position: 18},
+ experimental: {...cloneDeep(attributeState), position: 19},
+ locked_date: {...cloneDeep(attributeState), type:"url", position: 20},
+ map_type: {...cloneDeep(attributeState), position: 21},
+ external_id: {...cloneDeep(attributeState), position: 22},
+ owner: {...cloneDeep(attributeState), type: 'textFormatted', position: 23},
+ extras: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 24},
+ summary: {...cloneDeep(attributeState), collapsed: true, type: 'list', position: 25},
+ created_by: {...cloneDeep(attributeState), position: 26},
+ updated_by: {...cloneDeep(attributeState), position: 27},
+ created_on: {...cloneDeep(attributeState), type: 'date', position: 28},
+ updated_on: {...cloneDeep(attributeState), type: 'date', position: 29},
+ }
+
+ const fetcher = (uri, attr, loadingAttr, state) => {
+ if(uri && attr && loadingAttr) {
+ const { isVersion } = state;
+ const isAnyVersion = isVersion || uri.match(/\//g).length === 8;
+ return APIService
+ .new()
+ .overrideURL(encodeURI(uri))
+ .get(null, null, {includeSummary: true})
+ .then(response => {
+ if(get(response, 'status') === 200) {
+ const newState = {...state}
+ newState[attr] = formatter(response.data)
+ newState[loadingAttr] = false
+ newState.isVersion = isAnyVersion
+ newState.attributes = attributes
+ if(isAnyVersion) {
+ newState.attributes['is_latest_version'] = {...cloneDeep(attributeState), type: 'bool', position: 14}
+ newState.attributes['update_comment'] = {...cloneDeep(attributeState), position: 15}
+ }
+ return newState
+ }
+ })
+ }
+ }
+
+ const formatter = (source) => {
+ source.originalExtras = source.extras
+ source.originalSummary = source.summary
+ source.extras = toObjectArray(source.extras)
+ source.summary = toObjectArray(source.summary)
+ return source
+ }
+
+ const getAttributeValue = (source, attr, type) => {
+ let value = get(source, attr)
+ if (attr === 'extras' || attr === 'summary')
+ return JSON.stringify(value, undefined, 2)
+ if(type === 'list') {
+ if(isEmpty(value)) return '';
+ else return value
+ } else if(type === 'date') {
+ if(attr === 'created_on')
+ value ||= get(source, 'created_at')
+ if(attr === 'updated_on')
+ value ||= get(source, 'updated_at')
+ return value ? formatDate(value) : '';
+ } else if (type === 'textFormatted') {
+ if(attr === 'owner')
+ return `${source.owner_type}: ${source.owner}`
+ } else if (type === 'bool') {
+ return value ? 'True' : 'False'
+ } else {
+ if(includes(['created_by', 'updated_by'], attr))
+ value ||= get(source, `version_${attr}`)
+ if(attr === 'updated_by' && has(source, 'version_created_by'))
+ value ||= source.version_created_by
+ return value || '';
+ }
+ }
+
+ const getHeaderSubAttributeValues = (source, isVersion) => {
+ const attributes = [
+ {
+ name: "Source:",
+ value: source.source,
+ url: toParentURI(source.url)
+ },
+ {
+ name: "UID:",
+ value: source.id,
+ url: null
+ },
+ {
+ name: "Short Code:",
+ value: source.short_code,
+ url: null
+ },
+ ]
+ if (isVersion) {
+ attributes.push({
+ name: "VERSION:",
+ value: source.version,
+ url: null
+ })
+ }
+
+ return attributes
+ }
+
+ const getState = () => {
+ if (!location.state) return null
+ return {
+ isVersion: true,
+ isLoadingLHS: false,
+ isLoadingRHS: false,
+ lhs: formatter(location.state[0]),
+ rhs: formatter(location.state[1]),
+ drawer: false,
+ attributes
+ }
+ }
+
+ return
+}