Skip to content

Commit

Permalink
Merge pull request #2 from EyeSeeTea/feature/zebra-app-customisations
Browse files Browse the repository at this point in the history
Feature: add zebra app customisations (Map visualization)
  • Loading branch information
bhavananarayanan authored Oct 16, 2024
2 parents d0a6254 + d98d073 commit b41db33
Show file tree
Hide file tree
Showing 38 changed files with 1,923 additions and 35 deletions.
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,5 @@ src/locales/
cypress/videos/
cypress/screenshots/
cypress.env.json
/build
*.zip
12 changes: 10 additions & 2 deletions i18n/en.pot
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,16 @@ msgstr ""
"Content-Type: text/plain; charset=utf-8\n"
"Content-Transfer-Encoding: 8bit\n"
"Plural-Forms: nplurals=2; plural=(n != 1)\n"
"POT-Creation-Date: 2022-03-11T20:46:39.429Z\n"
"PO-Revision-Date: 2022-03-11T20:46:39.429Z\n"
"POT-Creation-Date: 2024-09-09T16:14:42.495Z\n"
"PO-Revision-Date: 2024-09-09T16:14:42.495Z\n"

msgid "Failed to load organisation units."
msgstr ""

msgid ""
"Failed to load program indicators using datastore information, please check "
"the URL parameters."
msgstr ""

msgid "Maps"
msgstr ""
Expand Down
18 changes: 9 additions & 9 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "maps-app",
"name": "zebra-custom-maps-app",
"version": "1.23.16",
"description": "DHIS2 Maps",
"description": "Zebra custom DHIS2 Maps",
"main": "src/app.js",
"repository": {
"type": "git",
"url": "git+https://github.com/dhis2/maps-app.git"
"url": "git+https://github.com/eyeseetea/maps-app.git"
},
"license": "BSD-3-Clause",
"author": "Bjørn Sandvik",
Expand All @@ -28,8 +28,8 @@
"start:nobrowser": "BROWSER=none yarn start",
"cy:open": "start-server-and-test 'yarn start:nobrowser' http://localhost:8082 'yarn cypress open --env networkMode=live'",
"cy:run": "start-server-and-test 'yarn start:nobrowser' http://localhost:8082 'yarn cypress run --env networkMode=live'",
"prebuild": "yarn test && yarn localize && yarn manifest && rm -rf build && mkdir build && cp -r public/* ./build/",
"build": "NODE_ENV=production webpack",
"prebuild": "yarn test && yarn localize && yarn manifest && rm -rf build/ && mkdir build && cp -r public/* ./build/",
"build": "NODE_ENV=production DHIS2_BASE_URL='' webpack && rm -f $npm_package_name.zip && cd build && zip --quiet -r ../$npm_package_name.zip *",
"build:netlify": "yarn build && d2-manifest package.json build/manifest.webapp --manifest.activities.dhis.href=\"https://play.dhis2.org/dev\"",
"manifest": "d2-manifest package.json public/manifest.webapp",
"lint": "eslint ./src",
Expand Down Expand Up @@ -128,20 +128,20 @@
"webpack-dev-server": "^3.11.2"
},
"manifest.webapp": {
"name": "DHIS2 Maps",
"name": "Zebra custom DHIS2 Maps",
"icons": {
"48": "icon.png"
},
"appType": "APP",
"core_app": true,
"short_name": "maps",
"core_app": false,
"short_name": "zebra-custom-maps-app",
"developer": {
"url": "",
"name": "Bjørn Sandvik"
},
"activities": {
"dhis": {
"href": ".."
"href": "*"
}
}
}
Expand Down
26 changes: 26 additions & 0 deletions src/actions/currentAppInfo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as types from '../constants/actionTypes';

/**
* @typedef {Object} CurrentAppInfo
* @property {string} app - The application identifier.
* @property {string} [page] - Optional. The current page identifier.
*/
/**
* Creates an action to set the current app info.
*
* @param {CurrentAppInfo} data - The object with the info for the current app with `app` and `page`.
* @returns {Object} The action object with type `types.CURRENT_APP_INFO_SET` and payload.
*/
export const setCurrentAppInfo = data => ({
type: types.CURRENT_APP_INFO_SET,
payload: data,
});

/**
* Creates an action to clean the current app info.
*
* @returns {Object} The action object with type `types.CURRENT_APP_INFO_CLEAN`.
*/
export const cleanCurrentAppInfo = () => ({
type: types.CURRENT_APP_INFO_CLEAN,
});
189 changes: 189 additions & 0 deletions src/actions/map.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,3 +68,192 @@ export const tOpenMap = (mapId, keyDefaultBaseMap, dataEngine) => async (
return e;
}
};

/**
* Opens a map with programIndicatorData filtered by orgUnitsInLayer, within a startDate and endDate range.
*
* @param {string} mapId - The ID of the map to load.
* @param {string} keyDefaultBaseMap - The key for the default base map.
* @param {DataEngine} dataEngine - DataEngine instance object.
* @param {Array.<string>|undefined} [orgUnitsInLayer] - Optional. List of organization unit ids to display in the map layer.
* @param {object|undefined} [programIndicatorData] - Optional. Program indicator data, containing the properties `id`, `name`, `programId`, and `programName`.
* @param {string} [programIndicatorData.id] - The ID of the program indicator.
* @param {string} [programIndicatorData.name] - The name of the program indicator.
* @param {string} [programIndicatorData.programId] - The ID of the program.
* @param {string} [programIndicatorData.programName] - The name of the program.
* @param {string|undefined} [startDate] - Optional. The start date in ISO format (YYYY-MM-DD).
* @param {string|undefined} [endDate] - Optional. The end date in ISO format (YYYY-MM-DD).
* @param {string} [timeField] - The time field to be used for filtering.
* @returns {Function} An async function that dispatches actions to configure the map and load the layer.
*/
export const tOpenProgramIndicatorMapWithOrgUnitsInLayerInStartEndDate = (
mapId,
keyDefaultBaseMap,
dataEngine,
orgUnitsInLayer,
programIndicatorData,
startDate,
endDate,
timeField
) => async (dispatch, getState) => {
try {
const map = await fetchMap(mapId, dataEngine, keyDefaultBaseMap);

const basemapConfig =
getState().basemaps.find(bm => bm.id === map.basemap.id) ||
getFallbackBasemap();

const basemap = { ...map.basemap, ...basemapConfig };

const mapViewsFilteredByOrgUnits = orgUnitsInLayer?.length
? filterMapViewsByOrgUnits(map.mapViews, orgUnitsInLayer)
: map.mapViews;

const mapViewsWithStartDateEndDate =
startDate && endDate
? addStartDateEndDateToMapViews(
mapViewsFilteredByOrgUnits,
startDate,
endDate
)
: mapViewsFilteredByOrgUnits;

const mapViewsWithProgramIndicator =
programIndicatorData.id &&
programIndicatorData.name &&
programIndicatorData.programId &&
programIndicatorData.programName
? setProgramIndicatorInMapViews(
mapViewsWithStartDateEndDate,
programIndicatorData
)
: mapViewsWithStartDateEndDate;

const filteredMap = {
...map,
mapViews: timeField
? mapViewsWithProgramIndicator.map(mapView => ({
...mapView,
timeField: timeField,
}))
: mapViewsWithProgramIndicator,
};

dispatch(setMap({ ...filteredMap, basemap }));
addOrgUnitPaths(filteredMap.mapViews).map(view =>
dispatch(loadLayer(view))
);
} catch (e) {
log.error(e);
return e;
}
};

function filterMapViewsByOrgUnits(mapViews, orgUnitsInLayer) {
if (!orgUnitsInLayer?.length || !mapViews?.length) {
return mapViews;
}

return mapViews.map(mapView => {
const filteredOrganisationUnits = orgUnitsInLayer.map(ou => ({
id: ou.id,
path: ou.path,
}));

const filteredParentGraphMap = orgUnitsInLayer.reduce((acc, ou) => {
return {
...acc,
[ou.id]: ou.parent.id,
};
}, {});

const filteredRows = mapView.rows?.map(row => {
return row.dimension === 'ou'
? {
...row,
items: orgUnitsInLayer.map(ou => ({
id: ou.id,
name: ou.displayName,
dimensionItemType: 'ORGANISATION_UNIT',
})),
}
: row;
});

return {
...mapView,
organisationUnits: filteredOrganisationUnits,
parentGraphMap: filteredParentGraphMap,
rows: filteredRows,
};
});
}

function addStartDateEndDateToMapViews(mapViews, startDate, endDate) {
if (!startDate || !endDate || !mapViews?.length) {
return mapViews;
}

return mapViews.map(mapView => {
return {
...mapView,
filters: [],
startDate: startDate,
endDate: endDate,
};
});
}

function setProgramIndicatorInMapViews(mapViews, programIndicatorData) {
if (
!programIndicatorData.id ||
!programIndicatorData.name ||
!programIndicatorData.programId ||
!programIndicatorData.programName ||
!mapViews?.length
) {
return mapViews;
}

return mapViews.map(mapView => {
const dataDimensionItemsWithProgramIndicator = mapView.dataDimensionItems?.map(
dataDimensionItem => {
return dataDimensionItem.dataDimensionItemType ===
'PROGRAM_INDICATOR'
? {
dataDimensionItemType:
dataDimensionItem.dataDimensionItemType,
programIndicator: { id: programIndicatorData.id },
}
: dataDimensionItem;
}
);

const columnsWithProgramIndicator = mapView.columns?.map(column => {
return column.dimension === 'dx'
? {
...column,
items: [
{
id: programIndicatorData.id,
name: programIndicatorData.name,
dimensionItemType: 'PROGRAM_INDICATOR',
},
],
}
: column;
});

const program = {
id: programIndicatorData.programId,
name: programIndicatorData.programName,
};

return {
...mapView,
program: program,
columns: columnsWithProgramIndicator,
dataDimensionItems: dataDimensionItemsWithProgramIndicator,
};
});
}
42 changes: 42 additions & 0 deletions src/actions/zebraCustomPopupData.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
import * as types from '../constants/actionTypes';

/**
* @typedef {Object} Filter
* @property {string} key - The key of the filter.
* @property {string} value - The value of the filter.
*/
/**
* Creates an action to load zebra custom popup data.
*
* @param {Array.<Filter>} filters - The filters to apply for loading the custom popup data.
* @returns {Object} The action object with type `types.ZEBRA_CUSTOM_POPUP_DATA_LOAD` and payload.
*/
export const loadZebraCustomPopupData = filters => ({
type: types.ZEBRA_CUSTOM_POPUP_DATA_LOAD,
payload: filters,
});

/**
* @typedef {Object} CustomPopupData
* @property {Object} data - The data to be set in the custom popup, where each key is an incident status and each value is an object with diseases and hazard types.
* @property {string} lastUpdatedDate - The date when the data was last updated, formatted as a string MM/DD/YYY.
*/
/**
* Creates an action to set zebra custom popup data.
*
* @param {CustomPopupData} data - The custom popup data to set, including data and last updated date.
* @returns {Object} The action object with type `types.ZEBRA_CUSTOM_POPUP_DATA_SET` and payload.
*/
export const setZebraCustomPopupData = data => ({
type: types.ZEBRA_CUSTOM_POPUP_DATA_SET,
payload: data,
});

/**
* Creates an action to clean zebra custom popup data.
*
* @returns {Object} The action object with type `types.ZEBRA_CUSTOM_POPUP_DATA_CLEAN`.
*/
export const cleanZebraCustomPopupData = () => ({
type: types.ZEBRA_CUSTOM_POPUP_DATA_CLEAN,
});
33 changes: 33 additions & 0 deletions src/actions/zebraProgramIndicators.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as types from '../constants/actionTypes';

/**
* Creates an action to load zebra program indicators.
*
* @param {Object} dataStoreInfo - Information required to load the zebra program indicators.
* @param {string} dataStoreInfo.zebraNamespace - The namespace in the datastore for zebra indicators.
* @param {string} dataStoreInfo.dashboardKey - The key in the datastore for retrieving dashboard program indicators info.
* @returns {Object} The action object with type `types.ZEBRA_PROGRAM_INDICATORS_LOAD` and the passed data as payload.
*/
export const loadZebraProgramIndicators = dataStoreInfo => ({
type: types.ZEBRA_PROGRAM_INDICATORS_LOAD,
payload: dataStoreInfo,
});

/**
* @typedef {Object} ProgramIndicator
* @property {string} id - The ID of the program indicator.
* @property {string} name - The name of the program indicator.
* @property {string|null} disease - The name of the disease associated with the indicator.
* @property {string|null} hazardType - The name of the hazardType associated with the indicator.
* @property {string|null} incidentStatus - The name of the incidentStatus associated with the indicator.
*/
/**
* Creates an action to set zebra program indicators.
*
* @param {Array.<ProgramIndicator>} programIndicators - The program indicators to set.
* @returns {Object} The action object with type `types.ZEBRA_PROGRAM_INDICATORS_SET` and payload.
*/
export const setZebraProgramIndicators = programIndicators => ({
type: types.ZEBRA_PROGRAM_INDICATORS_SET,
payload: programIndicators,
});
Loading

0 comments on commit b41db33

Please sign in to comment.