diff --git a/src/components/Visualization/StartScreen.js b/src/components/Visualization/StartScreen.js
index 1d2801e4dc..091ef63830 100644
--- a/src/components/Visualization/StartScreen.js
+++ b/src/components/Visualization/StartScreen.js
@@ -7,13 +7,12 @@ import React, { useEffect, useState } from 'react'
import { connect } from 'react-redux'
import { apiFetchMostViewedVisualizations } from '../../api/mostViewedVisualizations.js'
import { apiFetchVisualizations } from '../../api/visualization.js'
-import { GenericError } from '../../assets/ErrorIcons.js'
-import { VisualizationError, genericErrorTitle } from '../../modules/error.js'
import history from '../../modules/history.js'
import { sGetLoadError } from '../../reducers/loader.js'
import { sGetUsername } from '../../reducers/user.js'
import styles from './styles/StartScreen.module.css'
import { matchVisualizationWithType } from './utils.js'
+import { VisualizationErrorInfo } from './VisualizationErrorInfo.js'
const StartScreen = ({ error, username }) => {
const [mostViewedVisualizations, setMostViewedVisualizations] = useState([])
@@ -44,7 +43,7 @@ const StartScreen = ({ error, username }) => {
const getContent = () =>
error ? (
- getErrorContent()
+
) : (
{getContent()}
diff --git a/src/components/Visualization/Visualization.js b/src/components/Visualization/Visualization.js
index f25e90ee08..1c9f233526 100644
--- a/src/components/Visualization/Visualization.js
+++ b/src/components/Visualization/Visualization.js
@@ -1,8 +1,4 @@
-import {
- DIMENSION_ID_DATA,
- VIS_TYPE_OUTLIER_TABLE,
- VIS_TYPE_PIVOT_TABLE,
-} from '@dhis2/analytics'
+import { DIMENSION_ID_DATA, VIS_TYPE_PIVOT_TABLE } from '@dhis2/analytics'
import debounce from 'lodash-es/debounce'
import PropTypes from 'prop-types'
import React, { Component, Fragment } from 'react'
@@ -16,10 +12,10 @@ import {
acSetUiDataSorting,
acAddParentGraphMap,
} from '../../actions/ui.js'
+import { ensureAnalyticsResponsesContainData } from '../../modules/analytics.js'
import {
AssignedCategoriesDataElementsError,
GenericServerError,
- EmptyResponseError,
AssignedCategoriesAsFilterError,
MultipleIndicatorAsFilterError,
NoDataOrDataElementGroupSetError,
@@ -29,7 +25,6 @@ import {
ValueTypeError,
AnalyticsGenerationError,
AnalyticsRequestError,
- NoOutliersError,
} from '../../modules/error.js'
import { removeLastPathSegment } from '../../modules/orgUnit.js'
import { sGetCurrent } from '../../reducers/current.js'
@@ -144,15 +139,10 @@ export class UnconnectedVisualization extends Component {
this.props.addMetadata(forMetadata)
- if (
- !responses.some((response) => response.rows && response.rows.length)
- ) {
- if (this.props.visualization.type === VIS_TYPE_OUTLIER_TABLE) {
- throw new NoOutliersError()
- }
-
- throw new EmptyResponseError()
- }
+ ensureAnalyticsResponsesContainData(
+ responses,
+ this.props.visualization.type
+ )
}
onDrill = (drillData) => {
diff --git a/src/components/Visualization/VisualizationErrorInfo.js b/src/components/Visualization/VisualizationErrorInfo.js
new file mode 100644
index 0000000000..442fb4df28
--- /dev/null
+++ b/src/components/Visualization/VisualizationErrorInfo.js
@@ -0,0 +1,35 @@
+import PropTypes from 'prop-types'
+import React from 'react'
+import { GenericError } from '../../assets/ErrorIcons.js'
+import { VisualizationError, genericErrorTitle } from '../../modules/error.js'
+import styles from './styles/VisualizationErrorInfo.module.css'
+
+export const VisualizationErrorInfo = ({ error }) => (
+
+ {error instanceof VisualizationError ? (
+ <>
+
{error.icon()}
+
{error.title}
+
{error.description}
+ >
+ ) : (
+ <>
+
{GenericError()}
+
{genericErrorTitle}
+
+ {error.message || error}
+
+ >
+ )}
+
+)
+
+VisualizationErrorInfo.propTypes = {
+ error: PropTypes.oneOfType([
+ PropTypes.instanceOf(Error),
+ PropTypes.instanceOf(VisualizationError),
+ ]),
+}
diff --git a/src/components/Visualization/styles/StartScreen.module.css b/src/components/Visualization/styles/StartScreen.module.css
index 7b4f7331bc..60e075a384 100644
--- a/src/components/Visualization/styles/StartScreen.module.css
+++ b/src/components/Visualization/styles/StartScreen.module.css
@@ -13,34 +13,6 @@
display: flex;
align-items: center;
}
-.errorContainer {
- display: flex;
- flex-direction: column;
- align-items: center;
- text-align: center;
-}
-.errorIcon {
- width: 136px;
- height: 136px;
- margin: 0 auto var(--spacers-dp24);
-}
-.errorTitle {
- font-weight: 500;
- font-size: 20px;
- color: var(--colors-grey800);
- letter-spacing: 0.15px;
- line-height: 24px;
- width: 360px;
- margin: 0 auto var(--spacers-dp12);
-}
-.errorDescription {
- font-weight: 400;
- font-size: 14px;
- color: var(--colors-grey700);
- line-height: 19px;
- width: 360px;
- margin: 0 auto;
-}
.title {
font-weight: 500;
font-size: 17px;
diff --git a/src/components/Visualization/styles/VisualizationErrorInfo.module.css b/src/components/Visualization/styles/VisualizationErrorInfo.module.css
new file mode 100644
index 0000000000..b7c21e84ae
--- /dev/null
+++ b/src/components/Visualization/styles/VisualizationErrorInfo.module.css
@@ -0,0 +1,30 @@
+.errorContainer {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ text-align: center;
+ justify-content: center;
+ width: 100%;
+}
+.errorIcon {
+ width: 136px;
+ height: 136px;
+ margin: 0 auto var(--spacers-dp24);
+}
+.errorTitle {
+ font-weight: 500;
+ font-size: 20px;
+ color: var(--colors-grey800);
+ letter-spacing: 0.15px;
+ line-height: 24px;
+ width: 360px;
+ margin: 0 auto var(--spacers-dp12);
+}
+.errorDescription {
+ font-weight: 400;
+ font-size: 14px;
+ color: var(--colors-grey700);
+ line-height: 19px;
+ width: 360px;
+ margin: 0 auto;
+}
diff --git a/src/components/VisualizationPlugin/VisualizationPluginWrapper.js b/src/components/VisualizationPlugin/VisualizationPluginWrapper.js
index 3d4a1d10e4..18a1e4157f 100644
--- a/src/components/VisualizationPlugin/VisualizationPluginWrapper.js
+++ b/src/components/VisualizationPlugin/VisualizationPluginWrapper.js
@@ -1,12 +1,15 @@
import { CenteredContent, CircularLoader, ComponentCover } from '@dhis2/ui'
import PropTypes from 'prop-types'
import React, { useCallback, useEffect, useState } from 'react'
+import { ensureAnalyticsResponsesContainData } from '../../modules/analytics.js'
+import { VisualizationErrorInfo } from '../Visualization/VisualizationErrorInfo.js'
import { VisualizationPlugin } from '../VisualizationPlugin/VisualizationPlugin.js'
// handle internal state for features that need to work without the app's Redux store
const VisualizationPluginWrapper = (props) => {
const [pluginProps, setPluginProps] = useState(props)
const [isLoading, setIsLoading] = useState(true)
+ const [error, setError] = useState(null)
const onDataSorted = useCallback(
(sorting) => {
@@ -64,6 +67,23 @@ const VisualizationPluginWrapper = (props) => {
const onLoadingComplete = () => setIsLoading(false)
+ const onResponsesReceived = useCallback(
+ (responses) => {
+ try {
+ ensureAnalyticsResponsesContainData(
+ responses,
+ props.visualization.type
+ )
+ } catch (error) {
+ setError(error)
+ }
+ },
+ [props.visualization.type]
+ )
+
+ if (error) {
+ return
+ }
return (
<>
{isLoading && (
@@ -77,6 +97,7 @@ const VisualizationPluginWrapper = (props) => {
{...pluginProps}
onDataSorted={onDataSorted}
onLoadingComplete={onLoadingComplete}
+ onResponsesReceived={onResponsesReceived}
/>
>
)
diff --git a/src/modules/analytics.js b/src/modules/analytics.js
index 7773060f33..57dcb317a3 100644
--- a/src/modules/analytics.js
+++ b/src/modules/analytics.js
@@ -6,8 +6,10 @@ import {
DIMENSION_ID_PERIOD,
WEEKLY,
DAILY,
+ VIS_TYPE_OUTLIER_TABLE,
} from '@dhis2/analytics'
import i18n from '@dhis2/d2-i18n'
+import { EmptyResponseError, NoOutliersError } from './error.js'
export const outlierTableHeadersMap = {
[DIMENSION_ID_DATA]: 'dxname',
@@ -281,3 +283,19 @@ export const getRelativePeriodTypeUsed = (periodItems) => {
return DAILY
}
}
+
+export const ensureAnalyticsResponsesContainData = (
+ responses,
+ visualizationType
+) => {
+ const hasPopulatedRows = responses.some(
+ (response) => response.rows && response.rows.length > 0
+ )
+ if (!hasPopulatedRows) {
+ if (visualizationType === VIS_TYPE_OUTLIER_TABLE) {
+ throw new NoOutliersError()
+ }
+
+ throw new EmptyResponseError()
+ }
+}