diff --git a/src/components/Layout/DefaultLayout/DefaultAxis.js b/src/components/Layout/DefaultLayout/DefaultAxis.js index 387299871..abc482bdd 100644 --- a/src/components/Layout/DefaultLayout/DefaultAxis.js +++ b/src/components/Layout/DefaultLayout/DefaultAxis.js @@ -1,8 +1,3 @@ -import { - DIMENSION_TYPE_DATA_ELEMENT, - DIMENSION_TYPE_ORGANISATION_UNIT, - DIMENSION_TYPE_PERIOD, -} from '@dhis2/analytics' import { useDroppable } from '@dnd-kit/core' import { SortableContext } from '@dnd-kit/sortable' import cx from 'classnames' @@ -11,12 +6,7 @@ import React from 'react' import { useSelector, useDispatch } from 'react-redux' import { acSetUiOpenDimensionModal } from '../../../actions/ui.js' import { getAxisName } from '../../../modules/axis.js' -import { DIMENSION_TYPE_STATUS } from '../../../modules/dimensionConstants.js' -import { extractDimensionIdParts } from '../../../modules/utils.js' -import { - OUTPUT_TYPE_ENROLLMENT, - OUTPUT_TYPE_TRACKED_ENTITY, -} from '../../../modules/visualization.js' +import { getDimensionsWithSuffix } from '../../../modules/utils.js' import { sGetMetadata } from '../../../reducers/metadata.js' import { sGetUiDraggingId, @@ -29,76 +19,6 @@ import Chip from '../Chip.js' import { DropZone } from './DropZone.js' import styles from './styles/DefaultAxis.module.css' -export const getDimensionsWithSuffix = ({ - dimensionIds, - metadata, - inputType, -}) => { - const dimensions = dimensionIds.map((id) => { - const { dimensionId, programStageId, programId } = - extractDimensionIdParts(id, inputType) - const dimension = { - ...metadata[id], - dimensionId, - programStageId, - programId, - } - return dimension - }) - - if ( - [OUTPUT_TYPE_ENROLLMENT, OUTPUT_TYPE_TRACKED_ENTITY].includes(inputType) - ) { - const dimensionsWithSuffix = dimensions.map((dimension) => { - if ( - [ - DIMENSION_TYPE_DATA_ELEMENT, - DIMENSION_TYPE_ORGANISATION_UNIT, - DIMENSION_TYPE_STATUS, - DIMENSION_TYPE_PERIOD, - ].includes(dimension.dimensionType) - ) { - const duplicates = dimensions.filter( - (d) => - d.dimensionId === dimension.dimensionId && - d !== dimension && - ((dimension.programId && d.programId) || - (dimension.programStageId && d.programStageId)) - ) - - if (duplicates.length > 0) { - const sameProgramId = duplicates.find( - (dup) => dup.programId === dimension.programId - ) - const thirdPartyDuplicates = duplicates - .filter((dup) => dup.programId !== dimension.programId) - .find((dpid) => - duplicates.find( - (dup) => - dup.programStageId !== - dpid.programStageId && - dup.programId === dpid.programId - ) - ) - - if (sameProgramId || thirdPartyDuplicates) { - dimension.suffix = - metadata[dimension.programStageId].name - } else { - dimension.suffix = metadata[dimension.programId].name - } - } - } - - return dimension - }) - - return dimensionsWithSuffix - } - - return dimensions -} - const DefaultAxis = ({ axisId, className }) => { const lastDropZoneId = getDropzoneId(axisId, LAST) const { over, setNodeRef } = useDroppable({ diff --git a/src/components/Visualization/useAnalyticsData.js b/src/components/Visualization/useAnalyticsData.js index 927b608a9..565d91765 100644 --- a/src/components/Visualization/useAnalyticsData.js +++ b/src/components/Visualization/useAnalyticsData.js @@ -31,12 +31,14 @@ import { isAoWithTimeDimension } from '../../modules/timeDimensions.js' import { extractDimensionIdParts, formatDimensionId, + getDimensionsWithSuffix, } from '../../modules/utils.js' import { OUTPUT_TYPE_ENROLLMENT, OUTPUT_TYPE_EVENT, OUTPUT_TYPE_TRACKED_ENTITY, getHeadersMap, + headersMap, } from '../../modules/visualization.js' const analyticsApiEndpointMap = { @@ -279,31 +281,54 @@ const fetchLegendSets = async ({ legendSetIds, dataEngine }) => { return legendSets } -const extractHeaders = (analyticsResponse) => - analyticsResponse.headers.map((header, index) => { +const extractHeaders = (analyticsResponse, outputType) => { + const dimensionIds = analyticsResponse.headers.map((header) => { + const { dimensionId, programStageId, programId } = + extractDimensionIdParts(header.name, outputType) + const idMatch = Object.keys(headersMap).find( + (key) => headersMap[key] === dimensionId + ) + + return formatDimensionId({ + dimensionId: idMatch || dimensionId, + programStageId, + programId, + outputType, + }) + }) + + const dimensionsWithSuffix = getDimensionsWithSuffix({ + dimensionIds, + metadata: analyticsResponse.metaData.items, + inputType: outputType, + }) + + const labels = dimensionsWithSuffix.map(({ name, suffix, id }) => ({ + id, + label: suffix ? `${name}, ${suffix}` : name, + })) + + const headers = analyticsResponse.headers.map((header, index) => { const result = { ...header, index } const { dimensionId, programId, programStageId } = - extractDimensionIdParts(header.name) - if ( - programStageId && - analyticsResponse.headers.filter((h) => - h.name.includes(dimensionId) - ).length > 1 - ) { - result.column += ` - ${analyticsResponse.metaData.items[programStageId].name}` - } else { - if ( - programId && - analyticsResponse.headers.filter((h) => - h.name.includes(dimensionId) - ).length > 1 - ) { - result.column += ` - ${analyticsResponse.metaData.items[programId].name}` - } - } + extractDimensionIdParts(header.name, outputType) + + result.column = + labels.find( + (label) => + label.id === + formatDimensionId({ + dimensionId, + programId, + programStageId, + outputType, + }) + )?.label || result.column return result }) + return headers +} const extractRows = (analyticsResponse, headers) => { const filteredRows = [] @@ -390,7 +415,10 @@ const useAnalyticsData = ({ visualization, displayProperty, }) - const headers = extractHeaders(analyticsResponse) + const headers = extractHeaders( + analyticsResponse, + visualization.outputType + ) const rows = extractRows(analyticsResponse, headers) const rowContext = extractRowContext(analyticsResponse) const pager = analyticsResponse.metaData.pager diff --git a/src/modules/tableValues.js b/src/modules/tableValues.js index b705313a6..b6282715e 100644 --- a/src/modules/tableValues.js +++ b/src/modules/tableValues.js @@ -60,23 +60,23 @@ const getHeaderText = ({ stageOffset, column } = {}) => { } if (Number.isInteger(stageOffset)) { - let postfix + let repetitionSuffix if (stageOffset === 0) { - postfix = i18n.t('most recent') + repetitionSuffix = i18n.t('most recent') } else if (stageOffset === 1) { - postfix = i18n.t('oldest') + repetitionSuffix = i18n.t('oldest') } else if (stageOffset > 1) { - postfix = i18n.t('oldest {{repeatEventIndex}}', { + repetitionSuffix = i18n.t('oldest {{repeatEventIndex}}', { repeatEventIndex: `+${stageOffset - 1}`, }) } else if (stageOffset < 0) { - postfix = i18n.t('most recent {{repeatEventIndex}}', { + repetitionSuffix = i18n.t('most recent {{repeatEventIndex}}', { repeatEventIndex: stageOffset, }) } - return `${column} (${postfix})` + return `${column} (${repetitionSuffix})` } return column diff --git a/src/modules/utils.js b/src/modules/utils.js index 2db92faa8..2aa06c991 100644 --- a/src/modules/utils.js +++ b/src/modules/utils.js @@ -1,5 +1,14 @@ +import { + DIMENSION_TYPE_DATA_ELEMENT, + DIMENSION_TYPE_ORGANISATION_UNIT, + DIMENSION_TYPE_PERIOD, +} from '@dhis2/analytics' import { useState, useEffect, useRef } from 'react' -import { OUTPUT_TYPE_TRACKED_ENTITY } from './visualization.js' +import { DIMENSION_TYPE_STATUS } from './dimensionConstants.js' +import { + OUTPUT_TYPE_ENROLLMENT, + OUTPUT_TYPE_TRACKED_ENTITY, +} from './visualization.js' const DEFAULT_USER_INPUT_DELAY = 500 @@ -63,3 +72,73 @@ export const extractDimensionIdParts = (id, inputType) => { repetitionIndex.substring(0, repetitionIndex.indexOf(']')), } } + +export const getDimensionsWithSuffix = ({ + dimensionIds, + metadata, + inputType, +}) => { + const dimensions = dimensionIds.map((id) => { + const { dimensionId, programStageId, programId } = + extractDimensionIdParts(id, inputType) + const dimension = { + ...metadata[id], + dimensionId, + programStageId, + programId, + } + return dimension + }) + + if ( + [OUTPUT_TYPE_ENROLLMENT, OUTPUT_TYPE_TRACKED_ENTITY].includes(inputType) + ) { + const dimensionsWithSuffix = dimensions.map((dimension) => { + if ( + [ + DIMENSION_TYPE_DATA_ELEMENT, + DIMENSION_TYPE_ORGANISATION_UNIT, + DIMENSION_TYPE_STATUS, + DIMENSION_TYPE_PERIOD, + ].includes(dimension.dimensionType) + ) { + const duplicates = dimensions.filter( + (d) => + d.dimensionId === dimension.dimensionId && + d !== dimension && + ((dimension.programId && d.programId) || + (dimension.programStageId && d.programStageId)) + ) + + if (duplicates.length > 0) { + const sameProgramId = duplicates.find( + (dup) => dup.programId === dimension.programId + ) + const thirdPartyDuplicates = duplicates + .filter((dup) => dup.programId !== dimension.programId) + .find((dpid) => + duplicates.find( + (dup) => + dup.programStageId !== + dpid.programStageId && + dup.programId === dpid.programId + ) + ) + + if (sameProgramId || thirdPartyDuplicates) { + dimension.suffix = + metadata[dimension.programStageId].name + } else { + dimension.suffix = metadata[dimension.programId].name + } + } + } + + return dimension + }) + + return dimensionsWithSuffix + } + + return dimensions +}