diff --git a/src/__demo__/PivotTable.event.stories.js b/src/__demo__/PivotTable.event.stories.js index e0fdd1e85..4ff446efb 100644 --- a/src/__demo__/PivotTable.event.stories.js +++ b/src/__demo__/PivotTable.event.stories.js @@ -13,6 +13,11 @@ import datetimeVisualization from './data/event/datetime.visualization.json' import emailDataHideNa from './data/event/email.data.hidena.json' import emailData from './data/event/email.data.json' import emailVisualization from './data/event/email.visualization.json' +import eventouData from './data/event/eventou.data.json' +import eventouVisualization from './data/event/eventou.visualization.json' +import eventstatusDataHideNa from './data/event/eventstatus.data.hidena.json' +import eventstatusData from './data/event/eventstatus.data.json' +import eventstatusVisualization from './data/event/eventstatus.visualization.json' import integerDataHideNa from './data/event/integer.data.hidena.json' import integerData from './data/event/integer.data.json' import integerVisualization from './data/event/integer.visualization.json' @@ -385,3 +390,54 @@ export const Yesonly = (_, { pivotTableOptions }) => { } Yesonly.storyName = 'Yesonly' + +export const EventstatusNA = (_, { pivotTableOptions }) => { + const visualization = { + ...eventstatusVisualization, + ...visualizationReset, + ...pivotTableOptions, + } + + return ( +
+ +
+ ) +} + +EventstatusNA.storyName = 'Eventstatus N/A' + +export const Eventstatus = (_, { pivotTableOptions }) => { + const visualization = { + ...eventstatusVisualization, + ...visualizationReset, + ...pivotTableOptions, + } + + return ( +
+ +
+ ) +} + +Eventstatus.storyName = 'Eventstatus' + +export const Eventou = (_, { pivotTableOptions }) => { + const visualization = { + ...eventouVisualization, + ...visualizationReset, + ...pivotTableOptions, + } + + return ( +
+ +
+ ) +} + +Eventou.storyName = 'Event orgunit' diff --git a/src/__demo__/data/event/eventou.data.json b/src/__demo__/data/event/eventou.data.json new file mode 100644 index 000000000..b91914b4e --- /dev/null +++ b/src/__demo__/data/event/eventou.data.json @@ -0,0 +1,123 @@ +{ + "headers": [ + { + "name": "A03MvHHogjR.ou", + "column": "Organisation unit", + "valueType": "ORGANISATION_UNIT", + "type": "org.hisp.dhis.organisationunit.OrganisationUnit", + "hidden": false, + "meta": true + }, + { + "name": "A03MvHHogjR.eventdate", + "column": "Report date", + "valueType": "DATE", + "type": "java.time.LocalDate", + "hidden": false, + "meta": true + }, + { + "name": "value", + "column": "Value", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + } + ], + "metaData": { + "items": { + "202510": { + "name": "202510" + }, + "202511": { + "name": "202511" + }, + "202512": { + "name": "202512" + }, + "202601": { + "name": "202601" + }, + "202602": { + "name": "202602" + }, + "202603": { + "name": "202603" + }, + "IpHINAT79UW": { + "name": "Child Programme" + }, + "ZzYYXq4fJie": { + "name": "Baby Postnatal" + }, + "USER_ORGUNIT": { + "organisationUnits": [ + "ImspTQPwCqd" + ] + }, + "A03MvHHogjR.ou": { + "name": "Organisation unit" + }, + "ImspTQPwCqd": { + "name": "Sierra Leone" + }, + "A03MvHHogjR.eventdate": { + "name": "Report date" + }, + "A03MvHHogjR": { + "name": "Birth" + } + }, + "dimensions": { + "A03MvHHogjR.eventdate": [ + "202510", + "202511", + "202512", + "202601", + "202602", + "202603" + ], + "pe": [], + "A03MvHHogjR.ou": [ + "ImspTQPwCqd" + ] + } + }, + "rowContext": {}, + "width": 3, + "rows": [ + [ + "ImspTQPwCqd", + "202510", + "973" + ], + [ + "ImspTQPwCqd", + "202511", + "932" + ], + [ + "ImspTQPwCqd", + "202512", + "858" + ], + [ + "ImspTQPwCqd", + "202601", + "694" + ], + [ + "ImspTQPwCqd", + "202602", + "629" + ], + [ + "ImspTQPwCqd", + "202603", + "694" + ] + ], + "headerWidth": 3, + "height": 6 +} \ No newline at end of file diff --git a/src/__demo__/data/event/eventou.visualization.json b/src/__demo__/data/event/eventou.visualization.json new file mode 100644 index 000000000..b75aad7d6 --- /dev/null +++ b/src/__demo__/data/event/eventou.visualization.json @@ -0,0 +1,139 @@ +{ + "name": "Event orgunit", + "created": "2025-07-02T10:27:50.483", + "lastUpdated": "2025-07-02T10:27:50.483", + "translations": [], + "createdBy": { + "id": "xE7jOejl9FI", + "code": null, + "name": "John Traore", + "displayName": "John Traore", + "username": "admin" + }, + "favorites": [], + "lastUpdatedBy": { + "id": "xE7jOejl9FI", + "code": null, + "name": "John Traore", + "displayName": "John Traore", + "username": "admin" + }, + "sharing": { + "owner": "xE7jOejl9FI", + "external": false, + "users": {}, + "userGroups": {}, + "public": "rw------" + }, + "regressionType": "NONE", + "displayDensity": "NORMAL", + "fontSize": "NORMAL", + "sortOrder": 0, + "topLimit": 0, + "hideEmptyRows": false, + "showHierarchy": false, + "completedOnly": false, + "skipRounding": false, + "dataDimensionItems": [], + "dataElementGroupSetDimensions": [], + "organisationUnitGroupSetDimensions": [], + "categoryDimensions": [], + "categoryOptionGroupSetDimensions": [], + "attributeDimensions": [], + "programIndicatorDimensions": [], + "subscribers": [], + "digitGroupSeparator": "SPACE", + "hideEmptyRowItems": "NONE", + "hideLegend": false, + "noSpaceBetweenColumns": false, + "cumulativeValues": false, + "percentStackedValues": false, + "showData": false, + "colTotals": true, + "rowTotals": true, + "rowSubTotals": true, + "colSubTotals": true, + "hideTitle": false, + "hideSubtitle": false, + "showDimensionLabels": true, + "interpretations": [], + "program": { + "enrollmentDateLabel": "Date of enrollment", + "incidentDateLabel": "Date of birth", + "id": "IpHINAT79UW", + "name": "Child Programme" + }, + "programStage": { + "executionDateLabel": "Report date", + "id": "A03MvHHogjR", + "name": "Birth" + }, + "dataType": "AGGREGATED_VALUES", + "outputType": "EVENT", + "collapseDataDimensions": false, + "hideNaData": false, + "simpleDimensions": [ + { + "parent": "COLUMN", + "dimension": "ou", + "values": [ + "ImspTQPwCqd" + ] + } + ], + "legacy": true, + "type": "PIVOT_TABLE", + "filters": [], + "parentGraphMap": { + "ImspTQPwCqd": "" + }, + "columns": [ + { + "items": [ + { + "name": "Sierra Leone", + "id": "ImspTQPwCqd" + } + ], + "dimension": "A03MvHHogjR.ou", + "programStage": { + "id": "A03MvHHogjR" + } + } + ], + "rows": [ + { + "items": [ + { + "dimensionItemType": "PERIOD", + "id": "LAST_6_MONTHS" + } + ], + "dimension": "A03MvHHogjR.eventdate", + "programStage": { + "id": "A03MvHHogjR" + } + } + ], + "displayName": "Event orgunit", + "access": { + "manage": true, + "externalize": true, + "write": true, + "read": true, + "update": true, + "delete": true + }, + "user": { + "id": "xE7jOejl9FI", + "code": null, + "name": "John Traore", + "displayName": "John Traore", + "username": "admin" + }, + "favorite": false, + "subscribed": false, + "displayFormName": "Event orgunit", + "id": "xLPn5T11Vd4", + "attributeValues": [] +} \ No newline at end of file diff --git a/src/__demo__/data/event/eventstatus.data.hidena.json b/src/__demo__/data/event/eventstatus.data.hidena.json new file mode 100644 index 000000000..3c5631b0e --- /dev/null +++ b/src/__demo__/data/event/eventstatus.data.hidena.json @@ -0,0 +1,122 @@ +{ + "headers": [ + { + "name": "A03MvHHogjR.ou", + "column": "Organisation unit", + "valueType": "ORGANISATION_UNIT", + "type": "org.hisp.dhis.organisationunit.OrganisationUnit", + "hidden": false, + "meta": true + }, + { + "name": "A03MvHHogjR.eventdate", + "column": "Report date", + "valueType": "DATE", + "type": "java.time.LocalDate", + "hidden": false, + "meta": true + }, + { + "name": "A03MvHHogjR.eventstatus", + "column": "Event status", + "valueType": "TEXT", + "type": "java.lang.String", + "hidden": false, + "meta": true + }, + { + "name": "value", + "column": "Value", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + } + ], + "metaData": { + "items": { + "202511": { + "name": "202511" + }, + "202512": { + "name": "202512" + }, + "202601": { + "name": "202601" + }, + "ImspTQPwCqd": { + "name": "Sierra Leone" + }, + "A03MvHHogjR.eventdate": { + "name": "Report date" + }, + "A03MvHHogjR.eventstatus": { + "name": "Event status" + }, + "A03MvHHogjR.eventstatus_ACTIVE": { + "name": "Active" + }, + "IpHINAT79UW": { + "name": "Child Programme" + }, + "ZzYYXq4fJie": { + "name": "Baby Postnatal" + }, + "USER_ORGUNIT": { + "organisationUnits": [ + "ImspTQPwCqd" + ] + }, + "A03MvHHogjR": { + "name": "Birth" + }, + "A03MvHHogjR.ou": { + "name": "Organisation unit" + } + }, + "dimensions": { + "A03MvHHogjR.eventdate": [ + "202511", + "202512", + "202601" + ], + "A03MvHHogjR.eventstatus": [ + "A03MvHHogjR.eventstatus_ACTIVE" + ], + "pe": [], + "A03MvHHogjR.ou": [ + "ImspTQPwCqd" + ] + } + }, + "rowContext": {}, + "width": 4, + "headerWidth": 4, + "height": 3, + "rows": [ + [ + "ImspTQPwCqd", + "202511", + "A03MvHHogjR.eventstatus_ACTIVE", + "925" + ], + [ + "ImspTQPwCqd", + "202512", + "A03MvHHogjR.eventstatus_ACTIVE", + "858" + ], + [ + "ImspTQPwCqd", + "202601", + "A03MvHHogjR.eventstatus_ACTIVE", + "690" + ], + [ + "ImspTQPwCqd", + "202511", + "", + "12" + ] + ] +} \ No newline at end of file diff --git a/src/__demo__/data/event/eventstatus.data.json b/src/__demo__/data/event/eventstatus.data.json new file mode 100644 index 000000000..21e306b6c --- /dev/null +++ b/src/__demo__/data/event/eventstatus.data.json @@ -0,0 +1,132 @@ +{ + "headers": [ + { + "name": "A03MvHHogjR.ou", + "column": "Organisation unit", + "valueType": "ORGANISATION_UNIT", + "type": "org.hisp.dhis.organisationunit.OrganisationUnit", + "hidden": false, + "meta": true + }, + { + "name": "A03MvHHogjR.eventdate", + "column": "Report date", + "valueType": "DATE", + "type": "java.time.LocalDate", + "hidden": false, + "meta": true + }, + { + "name": "A03MvHHogjR.eventstatus", + "column": "Event status", + "valueType": "TEXT", + "type": "java.lang.String", + "hidden": false, + "meta": true + }, + { + "name": "value", + "column": "Value", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + } + ], + "metaData": { + "items": { + "202511": { + "name": "202511" + }, + "202512": { + "name": "202512" + }, + "202601": { + "name": "202601" + }, + "ImspTQPwCqd": { + "name": "Sierra Leone" + }, + "A03MvHHogjR.eventdate": { + "name": "Report date" + }, + "A03MvHHogjR.eventstatus": { + "name": "Event status" + }, + "A03MvHHogjR.eventstatus_ACTIVE": { + "name": "Active" + }, + "IpHINAT79UW": { + "name": "Child Programme" + }, + "ZzYYXq4fJie": { + "name": "Baby Postnatal" + }, + "USER_ORGUNIT": { + "organisationUnits": [ + "ImspTQPwCqd" + ] + }, + "A03MvHHogjR": { + "name": "Birth" + }, + "A03MvHHogjR.ou": { + "name": "Organisation unit" + }, + "": { + "name": "No value", + "style": { + "fontStyle": "italic", + "color": "#6C7787", + "fontFamily": "monospace", + "letterSpacing": "-0.3px" + } + } + }, + "dimensions": { + "A03MvHHogjR.eventdate": [ + "202511", + "202512", + "202601" + ], + "A03MvHHogjR.eventstatus": [ + "A03MvHHogjR.eventstatus_ACTIVE", + "" + ], + "pe": [], + "A03MvHHogjR.ou": [ + "ImspTQPwCqd" + ] + } + }, + "rowContext": {}, + "width": 4, + "headerWidth": 4, + "height": 3, + "rows": [ + [ + "ImspTQPwCqd", + "202511", + "A03MvHHogjR.eventstatus_ACTIVE", + "925" + ], + [ + "ImspTQPwCqd", + "202512", + "A03MvHHogjR.eventstatus_ACTIVE", + "858" + ], + [ + "ImspTQPwCqd", + "202601", + "A03MvHHogjR.eventstatus_ACTIVE", + "690" + ], + [ + "ImspTQPwCqd", + "202511", + "", + "12" + ] + ] +} \ No newline at end of file diff --git a/src/__demo__/data/event/eventstatus.data.org.json b/src/__demo__/data/event/eventstatus.data.org.json new file mode 100644 index 000000000..23823ef9a --- /dev/null +++ b/src/__demo__/data/event/eventstatus.data.org.json @@ -0,0 +1,117 @@ +{ + "headers": [ + { + "name": "A03MvHHogjR.ou", + "column": "Organisation unit", + "valueType": "ORGANISATION_UNIT", + "type": "org.hisp.dhis.organisationunit.OrganisationUnit", + "hidden": false, + "meta": true + }, + { + "name": "A03MvHHogjR.eventdate", + "column": "Report date", + "valueType": "DATE", + "type": "java.time.LocalDate", + "hidden": false, + "meta": true + }, + { + "name": "A03MvHHogjR.eventstatus", + "column": "Event status", + "valueType": "TEXT", + "type": "java.lang.String", + "hidden": false, + "meta": true + }, + { + "name": "value", + "column": "Value", + "valueType": "NUMBER", + "type": "java.lang.Double", + "hidden": false, + "meta": false + } + ], + "metaData": { + "items": { + "202511": { + "name": "202511" + }, + "202512": { + "name": "202512" + }, + "202601": { + "name": "202601" + }, + "ImspTQPwCqd": { + "name": "Sierra Leone" + }, + "A03MvHHogjR.eventdate": { + "name": "Report date" + }, + "A03MvHHogjR.eventstatus": { + "name": "Event status" + }, + "IpHINAT79UW": { + "name": "Child Programme" + }, + "ZzYYXq4fJie": { + "name": "Baby Postnatal" + }, + "USER_ORGUNIT": { + "organisationUnits": [ + "ImspTQPwCqd" + ] + }, + "A03MvHHogjR": { + "name": "Birth" + }, + "A03MvHHogjR.ou": { + "name": "Organisation unit" + } + }, + "dimensions": { + "A03MvHHogjR.eventdate": [ + "202511", + "202512", + "202601" + ], + "A03MvHHogjR.eventstatus": [], + "pe": [], + "A03MvHHogjR.ou": [ + "ImspTQPwCqd" + ] + } + }, + "rowContext": {}, + "width": 4, + "headerWidth": 4, + "height": 3, + "rows": [ + [ + "ImspTQPwCqd", + "202511", + "ACTIVE", + "925" + ], + [ + "ImspTQPwCqd", + "202512", + "ACTIVE", + "858" + ], + [ + "ImspTQPwCqd", + "202601", + "ACTIVE", + "690" + ], + [ + "ImspTQPwCqd", + "202511", + "", + "12" + ] + ] +} \ No newline at end of file diff --git a/src/__demo__/data/event/eventstatus.visualization.json b/src/__demo__/data/event/eventstatus.visualization.json new file mode 100644 index 000000000..04dd512b6 --- /dev/null +++ b/src/__demo__/data/event/eventstatus.visualization.json @@ -0,0 +1,154 @@ +{ + "name": "Event status", + "created": "2025-07-02T10:27:50.483", + "lastUpdated": "2025-07-02T10:27:50.483", + "translations": [], + "createdBy": { + "id": "xE7jOejl9FI", + "code": null, + "name": "John Traore", + "displayName": "John Traore", + "username": "admin" + }, + "favorites": [], + "lastUpdatedBy": { + "id": "xE7jOejl9FI", + "code": null, + "name": "John Traore", + "displayName": "John Traore", + "username": "admin" + }, + "sharing": { + "owner": "xE7jOejl9FI", + "external": false, + "users": {}, + "userGroups": {}, + "public": "rw------" + }, + "regressionType": "NONE", + "displayDensity": "NORMAL", + "fontSize": "NORMAL", + "sortOrder": 0, + "topLimit": 0, + "hideEmptyRows": false, + "showHierarchy": false, + "completedOnly": false, + "skipRounding": false, + "dataDimensionItems": [], + "dataElementGroupSetDimensions": [], + "organisationUnitGroupSetDimensions": [], + "categoryDimensions": [], + "categoryOptionGroupSetDimensions": [], + "attributeDimensions": [], + "programIndicatorDimensions": [], + "subscribers": [], + "digitGroupSeparator": "SPACE", + "hideEmptyRowItems": "NONE", + "hideLegend": false, + "noSpaceBetweenColumns": false, + "cumulativeValues": false, + "percentStackedValues": false, + "showData": false, + "colTotals": true, + "rowTotals": true, + "rowSubTotals": true, + "colSubTotals": true, + "hideTitle": false, + "hideSubtitle": false, + "showDimensionLabels": true, + "interpretations": [], + "program": { + "enrollmentDateLabel": "Date of enrollment", + "incidentDateLabel": "Date of birth", + "id": "IpHINAT79UW", + "name": "Child Programme" + }, + "programStage": { + "executionDateLabel": "Report date", + "id": "A03MvHHogjR", + "name": "Birth" + }, + "dataType": "AGGREGATED_VALUES", + "outputType": "EVENT", + "collapseDataDimensions": false, + "hideNaData": false, + "simpleDimensions": [ + { + "parent": "COLUMN", + "dimension": "ou", + "values": [ + "ImspTQPwCqd" + ] + } + ], + "legacy": true, + "type": "PIVOT_TABLE", + "filters": [], + "parentGraphMap": { + "ImspTQPwCqd": "" + }, + "columns": [ + { + "items": [ + { + "name": "Sierra Leone", + "id": "ImspTQPwCqd" + } + ], + "dimension": "A03MvHHogjR.ou", + "programStage": { + "id": "A03MvHHogjR" + } + }, + { + "items": [], + "programStage": { + "id": "A03MvHHogjR" + }, + "dimension": "A03MvHHogjR.eventstatus" + } + ], + "rows": [ + { + "items": [ + { + "dimensionItemType": "PERIOD", + "id": "202512" + }, + { + "dimensionItemType": "PERIOD", + "id": "202511" + }, + { + "dimensionItemType": "PERIOD", + "id": "202510" + } + ], + "dimension": "A03MvHHogjR.eventdate", + "programStage": { + "id": "A03MvHHogjR" + } + } + ], + "displayName": "Email", + "access": { + "manage": true, + "externalize": true, + "write": true, + "read": true, + "update": true, + "delete": true + }, + "user": { + "id": "xE7jOejl9FI", + "code": null, + "name": "John Traore", + "displayName": "John Traore", + "username": "admin" + }, + "favorite": false, + "subscribed": false, + "displayFormName": "Email", + "id": "xLPn5T11Vd4", + "attributeValues": [] +} \ No newline at end of file diff --git a/src/__demo__/data/event/integer.data.json b/src/__demo__/data/event/integer.data.json index fc8d9f0e6..1a45381e8 100644 --- a/src/__demo__/data/event/integer.data.json +++ b/src/__demo__/data/event/integer.data.json @@ -112,7 +112,11 @@ } }, "dimensions": { - "pe": ["202503", "202504", "202505"], + "pe": [ + "202503", + "202504", + "202505" + ], "Zj7UnCAulEk.qrur9Dvnyt5": [ "Zj7UnCAulEk.qrur9Dvnyt5_0", "Zj7UnCAulEk.qrur9Dvnyt5_1", @@ -127,49 +131,231 @@ "Zj7UnCAulEk.qrur9Dvnyt5_10", "" ], - "ou": ["ImspTQPwCqd"] + "ou": [ + "ImspTQPwCqd" + ] } }, "rowContext": {}, "width": 4, "rows": [ - ["Zj7UnCAulEk.qrur9Dvnyt5_8", "ImspTQPwCqd", "202503", "47"], - ["Zj7UnCAulEk.qrur9Dvnyt5_3", "ImspTQPwCqd", "202505", "67"], - ["Zj7UnCAulEk.qrur9Dvnyt5_0", "ImspTQPwCqd", "202503", "55"], - ["Zj7UnCAulEk.qrur9Dvnyt5_5", "ImspTQPwCqd", "202503", "70"], - ["Zj7UnCAulEk.qrur9Dvnyt5_10", "ImspTQPwCqd", "202505", "61"], - ["Zj7UnCAulEk.qrur9Dvnyt5_7", "ImspTQPwCqd", "202505", "52"], - ["Zj7UnCAulEk.qrur9Dvnyt5_9", "ImspTQPwCqd", "202504", "54"], - ["Zj7UnCAulEk.qrur9Dvnyt5_6", "ImspTQPwCqd", "202505", "65"], - ["Zj7UnCAulEk.qrur9Dvnyt5_9", "ImspTQPwCqd", "202505", "54"], - ["Zj7UnCAulEk.qrur9Dvnyt5_6", "ImspTQPwCqd", "202503", "53"], - ["Zj7UnCAulEk.qrur9Dvnyt5_2", "ImspTQPwCqd", "202504", "45"], - ["Zj7UnCAulEk.qrur9Dvnyt5_10", "ImspTQPwCqd", "202503", "51"], - ["Zj7UnCAulEk.qrur9Dvnyt5_2", "ImspTQPwCqd", "202505", "51"], - ["Zj7UnCAulEk.qrur9Dvnyt5_3", "ImspTQPwCqd", "202504", "38"], - ["Zj7UnCAulEk.qrur9Dvnyt5_2", "ImspTQPwCqd", "202503", "41"], - ["Zj7UnCAulEk.qrur9Dvnyt5_8", "ImspTQPwCqd", "202504", "44"], - ["Zj7UnCAulEk.qrur9Dvnyt5_7", "ImspTQPwCqd", "202504", "40"], - ["Zj7UnCAulEk.qrur9Dvnyt5_4", "ImspTQPwCqd", "202505", "41"], - ["Zj7UnCAulEk.qrur9Dvnyt5_4", "ImspTQPwCqd", "202504", "42"], - ["Zj7UnCAulEk.qrur9Dvnyt5_9", "ImspTQPwCqd", "202503", "52"], - ["Zj7UnCAulEk.qrur9Dvnyt5_0", "ImspTQPwCqd", "202504", "55"], - ["Zj7UnCAulEk.qrur9Dvnyt5_8", "ImspTQPwCqd", "202505", "43"], - ["Zj7UnCAulEk.qrur9Dvnyt5_5", "ImspTQPwCqd", "202504", "63"], - ["Zj7UnCAulEk.qrur9Dvnyt5_6", "ImspTQPwCqd", "202504", "53"], - ["Zj7UnCAulEk.qrur9Dvnyt5_4", "ImspTQPwCqd", "202503", "54"], - ["Zj7UnCAulEk.qrur9Dvnyt5_10", "ImspTQPwCqd", "202504", "46"], - ["Zj7UnCAulEk.qrur9Dvnyt5_1", "ImspTQPwCqd", "202503", "44"], - ["Zj7UnCAulEk.qrur9Dvnyt5_7", "ImspTQPwCqd", "202503", "47"], - ["Zj7UnCAulEk.qrur9Dvnyt5_5", "ImspTQPwCqd", "202505", "46"], - ["Zj7UnCAulEk.qrur9Dvnyt5_0", "ImspTQPwCqd", "202505", "50"], - ["Zj7UnCAulEk.qrur9Dvnyt5_1", "ImspTQPwCqd", "202504", "48"], - ["Zj7UnCAulEk.qrur9Dvnyt5_1", "ImspTQPwCqd", "202505", "53"], - ["Zj7UnCAulEk.qrur9Dvnyt5_3", "ImspTQPwCqd", "202503", "56"], - ["", "ImspTQPwCqd", "202505", "5"], - ["", "ImspTQPwCqd", "202504", "12"], - ["", "ImspTQPwCqd", "202503", "10"] + [ + "Zj7UnCAulEk.qrur9Dvnyt5_8", + "ImspTQPwCqd", + "202503", + "47" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_3", + "ImspTQPwCqd", + "202505", + "67" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_0", + "ImspTQPwCqd", + "202503", + "55" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_5", + "ImspTQPwCqd", + "202503", + "70" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_10", + "ImspTQPwCqd", + "202505", + "61" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_7", + "ImspTQPwCqd", + "202505", + "52" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_9", + "ImspTQPwCqd", + "202504", + "54" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_6", + "ImspTQPwCqd", + "202505", + "65" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_9", + "ImspTQPwCqd", + "202505", + "54" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_6", + "ImspTQPwCqd", + "202503", + "53" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_2", + "ImspTQPwCqd", + "202504", + "45" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_10", + "ImspTQPwCqd", + "202503", + "51" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_2", + "ImspTQPwCqd", + "202505", + "51" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_3", + "ImspTQPwCqd", + "202504", + "38" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_2", + "ImspTQPwCqd", + "202503", + "41" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_8", + "ImspTQPwCqd", + "202504", + "44" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_7", + "ImspTQPwCqd", + "202504", + "40" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_4", + "ImspTQPwCqd", + "202505", + "41" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_4", + "ImspTQPwCqd", + "202504", + "42" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_9", + "ImspTQPwCqd", + "202503", + "52" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_0", + "ImspTQPwCqd", + "202504", + "55" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_8", + "ImspTQPwCqd", + "202505", + "43" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_5", + "ImspTQPwCqd", + "202504", + "63" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_6", + "ImspTQPwCqd", + "202504", + "53" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_4", + "ImspTQPwCqd", + "202503", + "54" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_10", + "ImspTQPwCqd", + "202504", + "46" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_1", + "ImspTQPwCqd", + "202503", + "44" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_7", + "ImspTQPwCqd", + "202503", + "47" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_5", + "ImspTQPwCqd", + "202505", + "46" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_0", + "ImspTQPwCqd", + "202505", + "50" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_1", + "ImspTQPwCqd", + "202504", + "48" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_1", + "ImspTQPwCqd", + "202505", + "53" + ], + [ + "Zj7UnCAulEk.qrur9Dvnyt5_3", + "ImspTQPwCqd", + "202503", + "56" + ], + [ + "", + "ImspTQPwCqd", + "202505", + "5" + ], + [ + "", + "ImspTQPwCqd", + "202504", + "12" + ], + [ + "", + "ImspTQPwCqd", + "202503", + "10" + ] ], "height": 267, "headerWidth": 4 -} +} \ No newline at end of file diff --git a/src/__demo__/data/event/integer.visualization.json b/src/__demo__/data/event/integer.visualization.json index 9f2d66272..3b127ab9f 100644 --- a/src/__demo__/data/event/integer.visualization.json +++ b/src/__demo__/data/event/integer.visualization.json @@ -87,7 +87,9 @@ { "parent": "COLUMN", "dimension": "ou", - "values": ["ImspTQPwCqd"] + "values": [ + "ImspTQPwCqd" + ] } ], "legacy": true, diff --git a/src/index.js b/src/index.js index 976210f68..eabb4c277 100644 --- a/src/index.js +++ b/src/index.js @@ -334,6 +334,10 @@ export { renderValue as formatValue } from './modules/renderValue.js' export { transformResponse as transformEventAggregateResponse } from './modules/response/event/response.js' +// Modules: eventVisualization + +export { transformEventVisualization } from './modules/eventVisualization/eventVisualization.js' + // Utils: colorSets export { COLOR_SET_DEFAULT, diff --git a/src/modules/dimensions.js b/src/modules/dimensions.js new file mode 100644 index 000000000..71e62ce06 --- /dev/null +++ b/src/modules/dimensions.js @@ -0,0 +1,74 @@ +// vis: 'dimension' in EventVisualization columns/rows/filters +// dim: tracker analytics api dimension/filter id +// header: tracker analytics api header name (query endpoints) + +const EVENT_DIMENSIONS = [ + { + vis: 'ou', + dim: 'ou', + header: 'ouname', + }, + { + vis: 'eventDate', + dim: 'EVENT_DATE', + header: 'eventdate', + }, + { + vis: 'enrollmentDate', + dim: 'ENROLLMENT_DATE', + header: 'enrollmentdate', + }, + { + vis: 'scheduledDate', + dim: 'SCHEDULED_DATE', + header: 'scheduleddate', + }, + { + vis: 'incidentDate', + dim: 'INCIDENT_DATE', + header: 'incidentdate', + }, + { + vis: 'lastUpdated', + dim: 'LAST_UPDATED', + header: 'lastupdated', + }, + { + vis: 'created', + dim: 'CREATED', + header: 'created', + }, + { + vis: 'completed', + dim: 'COMPLETED', + header: 'completed', + }, + { + vis: 'eventStatus', + dim: 'EVENT_STATUS', + header: 'eventstatus', + }, + { + vis: 'programStatus', + dim: 'PROGRAM_STATUS', + header: 'programstatus', + }, + { + vis: 'enrollmentOu', + dim: 'ENROLLMENT_OU', + header: 'enrollmentouname', + }, + { + vis: 'createdBy', + dim: '', + header: 'createdbydisplayname', + }, + { + vis: 'lastUpdatedBy', + dim: '', + header: 'lastupdatedbydisplayname', + }, +] + +export const getHeaderByVis = (vis) => + EVENT_DIMENSIONS.find((d) => d.vis === vis)?.header diff --git a/src/modules/eventVisualization/__tests__/eventVisualization.spec.js b/src/modules/eventVisualization/__tests__/eventVisualization.spec.js new file mode 100644 index 000000000..8fb07d9d8 --- /dev/null +++ b/src/modules/eventVisualization/__tests__/eventVisualization.spec.js @@ -0,0 +1,48 @@ +import { transformEventVisualization } from '../eventVisualization.js' + +const testDim1 = { + dimension: 'eventDate', + programStage: { + id: 'A03MvHHogjR', + }, +} + +const testDim2 = { + dimension: 'enrollmentDate', + program: { + id: 'IpHINAT79UW', + }, +} + +const testDim3 = { + dimension: 'created', +} + +const testAxis = [testDim1] + +const testVis = { + columns: testAxis, + rows: [testDim2], + filters: [testDim3], +} + +describe('eventVisualization', () => { + describe('transformEventVisualization', () => { + it('does not modify dimension, axis or vis', () => { + const newVis = transformEventVisualization(testVis) + expect(newVis).not.toBe(testVis) + expect(newVis.columns).not.toBe(testAxis) + expect(newVis.columns[0]).not.toBe(testDim1) + }) + + it('applies program stage to id', () => { + const newVis = transformEventVisualization(testVis) + expect(newVis.columns[0].dimension).toBe('A03MvHHogjR.eventdate') + }) + + it('applies program to id', () => { + const newVis = transformEventVisualization(testVis) + expect(newVis.rows[0].dimension).toBe('IpHINAT79UW.enrollmentdate') + }) + }) +}) diff --git a/src/modules/eventVisualization/eventVisualization.js b/src/modules/eventVisualization/eventVisualization.js new file mode 100644 index 000000000..520fd3ba8 --- /dev/null +++ b/src/modules/eventVisualization/eventVisualization.js @@ -0,0 +1,26 @@ +import { getHeaderByVis } from '../dimensions.js' +import { layoutGetAllDimensions } from '../layout/layoutGetAllDimensions.js' + +// Dimensions saved with program or program stage in an EventVisualization need +// transformation before we can pass them to the pivot table engine + +const cloneAxis = (axis) => axis?.map((dim) => ({ ...dim })) + +export const transformEventVisualization = (vis) => { + // Do not modify the original visualization + const transformedVis = { + ...vis, + columns: cloneAxis(vis.columns), + rows: cloneAxis(vis.rows), + filters: cloneAxis(vis.filters), + } + + layoutGetAllDimensions(transformedVis).forEach((dim) => { + const headerName = getHeaderByVis(dim.dimension) + const prefix = dim.program?.id ?? dim.programStage?.id + + dim.dimension = prefix ? `${prefix}.${headerName}` : headerName + }) + + return transformedVis +} diff --git a/src/modules/response/event/__tests__/response.spec.js b/src/modules/response/event/__tests__/response.spec.js index 8566ef653..452391d05 100644 --- a/src/modules/response/event/__tests__/response.spec.js +++ b/src/modules/response/event/__tests__/response.spec.js @@ -10,6 +10,9 @@ import responseDatetimeOrg from '../../../../__demo__/data/event/datetime.data.o import responseTextHideNa from '../../../../__demo__/data/event/email.data.hidena.json' import responseText from '../../../../__demo__/data/event/email.data.json' import responseTextOrg from '../../../../__demo__/data/event/email.data.org.json' +import responseEventstatusHideNa from '../../../../__demo__/data/event/eventstatus.data.hidena.json' +import responseEventstatus from '../../../../__demo__/data/event/eventstatus.data.json' +import responseEventstatusOrg from '../../../../__demo__/data/event/eventstatus.data.org.json' import responseNumericHideNa from '../../../../__demo__/data/event/integer.data.hidena.json' import responseNumeric from '../../../../__demo__/data/event/integer.data.json' import responseNumericOrg from '../../../../__demo__/data/event/integer.data.org.json' @@ -30,9 +33,36 @@ import { VALUE_TYPE_PERCENTAGE, VALUE_TYPE_TRUE_ONLY, } from '../../../valueTypes.js' -import { getItemFormatterByValueType, transformResponse } from '../response.js' +import { + getItemFormatterByHeaderName, + getItemFormatterByValueType, + transformResponse, +} from '../response.js' describe('response', () => { + describe('getItemFormatterByHeaderName', () => { + it('should return the correct formatter and format correctly', () => { + expect(getItemFormatterByHeaderName('eventstatus')('ACTIVE')).toBe( + 'Active' + ) + expect( + getItemFormatterByHeaderName('eventstatus')('COMPLETED') + ).toBe('Completed') + expect( + getItemFormatterByHeaderName('eventstatus')('SCHEDULE') + ).toBe('Scheduled') + expect( + getItemFormatterByHeaderName('programstatus')('ACTIVE') + ).toBe('Active') + expect( + getItemFormatterByHeaderName('programstatus')('COMPLETED') + ).toBe('Completed') + expect( + getItemFormatterByHeaderName('programstatus')('CANCELLED') + ).toBe('Cancelled') + }) + }) + describe('getItemFormatterByValueType', () => { it('should return the correct formatter and format correctly', () => { expect(getItemFormatterByValueType(VALUE_TYPE_BOOLEAN)('1')).toBe( @@ -178,5 +208,21 @@ describe('response', () => { ).toEqual(responseYesOnlyHideNa) }) }) + + describe('eventstatus', () => { + it('transforms response', () => { + expect(transformResponse(responseEventstatusOrg)).toEqual( + responseEventstatus + ) + }) + + it('transforms response and hides N/A data', () => { + expect( + transformResponse(responseEventstatusOrg, { + hideNaData: true, + }) + ).toEqual(responseEventstatusHideNa) + }) + }) }) }) diff --git a/src/modules/response/event/response.js b/src/modules/response/event/response.js index b25f8ad7b..35c62eeb3 100644 --- a/src/modules/response/event/response.js +++ b/src/modules/response/event/response.js @@ -20,6 +20,11 @@ import { import { applyDefaultHandler } from './default.js' import { applyOptionSetHandler } from './optionSet.js' +// Responses coming from these endpoints need transformation +// before we can pass it to the pivot table engine: +// - analytics/events/aggregate +// - analytics/enrollments/aggregate + export const PREFIX_SEPARATOR = '_' export const NA_VALUE = '' export const NA_VALUE_ITEM = { @@ -41,6 +46,23 @@ export const UNSUPPORTED_VALUE_TYPES = [ VALUE_TYPE_REFERENCE, ] +const STATUSES = { + ACTIVE: i18n.t('Active'), + COMPLETED: i18n.t('Completed'), + SCHEDULE: i18n.t('Scheduled'), + CANCELLED: i18n.t('Cancelled'), +} + +const formatStatus = (value) => STATUSES[value] || value + +export const getItemFormatter = ({ name, valueType }) => + getItemFormatterByHeaderName(name) || getItemFormatterByValueType(valueType) + +export const getItemFormatterByHeaderName = (name) => + name.endsWith('eventstatus') || name.endsWith('programstatus') + ? formatStatus + : undefined + export const getItemFormatterByValueType = (valueType) => { switch (valueType) { case VALUE_TYPE_AGE: @@ -58,6 +80,27 @@ export const getItemFormatterByValueType = (valueType) => { } } +const EXCLUDED_HEADER_NAMES = new Set([ + DIMENSION_ID_PERIOD, + DIMENSION_ID_ORGUNIT, + 'lastupdated', + 'created', + 'completed', +]) + +const EXCLUDED_HEADER_SUFFIXES = [ + '.eventdate', + '.enrollmentdate', + '.scheduleddate', + '.incidentdate', + '.ou', +] + +const isIncludedHeader = (header) => + Boolean(header.meta) && + !EXCLUDED_HEADER_NAMES.has(header.name) && + !EXCLUDED_HEADER_SUFFIXES.some((suffix) => header.name.endsWith(suffix)) + export const transformResponse = (response, { hideNaData = false } = {}) => { // Do not modify the original response // Rows is mapped by the handlers @@ -81,13 +124,7 @@ export const transformResponse = (response, { hideNaData = false } = {}) => { ...header, index, })) - .filter( - (header) => - Boolean(header.meta) && - ![DIMENSION_ID_PERIOD, DIMENSION_ID_ORGUNIT].includes( - header.name - ) - ) + .filter(isIncludedHeader) // Legendsets use uids and do not need transformation // Skip unsupported value types @@ -110,9 +147,7 @@ export const transformResponse = (response, { hideNaData = false } = {}) => { transformedResponse, header.index, { - itemFormatter: getItemFormatterByValueType( - header.valueType - ), + itemFormatter: getItemFormatter(header), } ) }