diff --git a/i18n/en.pot b/i18n/en.pot index 71c7fd25c..57aa4da11 100644 --- a/i18n/en.pot +++ b/i18n/en.pot @@ -5,8 +5,8 @@ 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: 2024-02-12T09:30:18.362Z\n" -"PO-Revision-Date: 2024-02-12T09:30:18.362Z\n" +"POT-Creation-Date: 2024-02-15T15:29:35.070Z\n" +"PO-Revision-Date: 2024-02-15T15:29:35.070Z\n" msgid "Untitled map, {{date}}" msgstr "Untitled map, {{date}}" @@ -263,6 +263,9 @@ msgstr "org unit" msgid "Earth Engine" msgstr "Earth Engine" +msgid "feature" +msgstr "feature" + msgid "Edit {{name}} layer" msgstr "Edit {{name}} layer" @@ -394,6 +397,18 @@ msgstr "Radius in meters" msgid "Buffer can't be combined with associated geometry." msgstr "Buffer can't be combined with associated geometry." +msgid "Fill color" +msgstr "Fill color" + +msgid "Line/stroke color" +msgstr "Line/stroke color" + +msgid "Line/stroke width" +msgstr "Line/stroke width" + +msgid "Point size" +msgstr "Point size" + msgid "Labels" msgstr "Labels" @@ -487,9 +502,6 @@ msgstr "Display Tracked Entity relationships" msgid "Tracked entity style" msgstr "Tracked entity style" -msgid "Point size" -msgstr "Point size" - msgid "Related entity style" msgstr "Related entity style" @@ -502,6 +514,9 @@ msgstr "No relationship types were found for tracked entity type {{type}}" msgid "Relationship type" msgstr "Relationship type" +msgid "Feature profile" +msgstr "Feature profile" + msgid "Remove filter" msgstr "Remove filter" @@ -605,6 +620,9 @@ msgstr "Group" msgid "Filters" msgstr "Filters" +msgid "Failed to load map layer \"{{layername}}\": {{message}}" +msgstr "Failed to load map layer \"{{layername}}\": {{message}}" + msgid "Drill up one level" msgstr "Drill up one level" @@ -1301,6 +1319,12 @@ msgstr "Facilities" msgid "Facilities: No coordinates found" msgstr "Facilities: No coordinates found" +msgid "Authorization is no longer valid. Please contact your administrator." +msgstr "Authorization is no longer valid. Please contact your administrator." + +msgid "Failed to load layer: {{error}}" +msgstr "Failed to load layer: {{error}}" + msgid "Organisation units" msgstr "Organisation units" diff --git a/package.json b/package.json index dc87ea8d9..9d9aa7673 100644 --- a/package.json +++ b/package.json @@ -20,6 +20,7 @@ "postinstall": "patch-package" }, "devDependencies": { + "@babel/plugin-proposal-private-property-in-object": "^7.21.11", "@dhis2/cli-app-scripts": "^10.4.0", "@dhis2/cli-style": "^10.5.1", "@dhis2/cypress-commands": "^10.0.3", diff --git a/patches/webpack+5.76.2.patch b/patches/webpack+5.90.1.patch similarity index 79% rename from patches/webpack+5.76.2.patch rename to patches/webpack+5.90.1.patch index 44c61dd17..f080819ba 100644 --- a/patches/webpack+5.76.2.patch +++ b/patches/webpack+5.90.1.patch @@ -1,13 +1,14 @@ diff --git a/node_modules/webpack/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js b/node_modules/webpack/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js -index b9947d6..31273c5 100644 +index a17b92b..2196342 100644 --- a/node_modules/webpack/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js +++ b/node_modules/webpack/lib/webworker/ImportScriptsChunkLoadingRuntimeModule.js -@@ -88,6 +88,19 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { +@@ -88,6 +88,20 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { + const stateExpression = withHmr ? `${RuntimeGlobals.hmrRuntimeStatePrefix}_importScripts` : undefined; - -+ const outputName = this.compilation.getPath( -+ getChunkFilenameTemplate(chunk, this.compilation.outputOptions), ++ ++ const outputName = compilation.getPath( ++ getChunkFilenameTemplate(chunk, compilation.outputOptions), + { + chunk, + contentHashType: "javascript" @@ -15,14 +16,14 @@ index b9947d6..31273c5 100644 + ); + const rootOutputDir = JSON.stringify(getUndoPath( + outputName, -+ this.compilation.outputOptions.path, ++ /** @type {string} */ (compilation.outputOptions.path), + true + )); + - return Template.asString([ - withBaseURI ? this._generateBaseUri(chunk) : "// no baseURI", - "", -@@ -142,8 +155,8 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { + const runtimeTemplate = compilation.runtimeTemplate; + const { _withCreateScriptUrl: withCreateScriptUrl } = this; + +@@ -145,8 +159,8 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { Template.indent( `importScripts(${ withCreateScriptUrl @@ -33,7 +34,7 @@ index b9947d6..31273c5 100644 });` ), "}" -@@ -183,8 +196,8 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { +@@ -186,8 +200,8 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { "// start update chunk loading", `importScripts(${ withCreateScriptUrl @@ -44,7 +45,7 @@ index b9947d6..31273c5 100644 });`, 'if(!success) throw new Error("Loading update chunk failed for unknown reason");' ]), -@@ -221,7 +234,7 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { +@@ -224,7 +238,7 @@ class ImportScriptsChunkLoadingRuntimeModule extends RuntimeModule { RuntimeGlobals.hmrDownloadManifest } = ${runtimeTemplate.basicFunction("", [ 'if (typeof fetch === "undefined") throw new Error("No browser support: need fetch API");', diff --git a/public/images/featurelayer.png b/public/images/featurelayer.png new file mode 100644 index 000000000..05f634685 Binary files /dev/null and b/public/images/featurelayer.png differ diff --git a/src/AppWrapper.js b/src/AppWrapper.js index 282a0fa8b..3c74bbff4 100644 --- a/src/AppWrapper.js +++ b/src/AppWrapper.js @@ -106,7 +106,12 @@ const AppWrapper = () => { > - + diff --git a/src/actions/dataTable.js b/src/actions/dataTable.js index e904d455c..392ebadf4 100644 --- a/src/actions/dataTable.js +++ b/src/actions/dataTable.js @@ -1,10 +1,5 @@ import * as types from '../constants/actionTypes.js' -export const openDataTable = (id) => ({ - type: types.DATA_TABLE_OPEN, - id, -}) - export const closeDataTable = () => ({ type: types.DATA_TABLE_CLOSE, }) diff --git a/src/actions/feature.js b/src/actions/feature.js index 5d217d969..4c54bdc3c 100644 --- a/src/actions/feature.js +++ b/src/actions/feature.js @@ -4,3 +4,12 @@ export const highlightFeature = (payload) => ({ type: types.FEATURE_HIGHLIGHT, payload, }) + +export const setFeatureProfile = (payload) => ({ + type: types.FEATURE_PROFILE_SET, + payload, +}) + +export const closeFeatureProfile = () => ({ + type: types.FEATURE_PROFILE_CLOSE, +}) diff --git a/src/actions/layerEdit.js b/src/actions/layerEdit.js index 80037f548..27799293c 100644 --- a/src/actions/layerEdit.js +++ b/src/actions/layerEdit.js @@ -358,3 +358,9 @@ export const setNoDataColor = (color) => ({ type: types.LAYER_EDIT_NO_DATA_COLOR_SET, payload: color, }) + +// Set feature style +export const setFeatureStyle = (payload) => ({ + type: types.LAYER_EDIT_FEATURE_STYLE_SET, + payload, +}) diff --git a/src/components/app/DetailsPanel.js b/src/components/app/DetailsPanel.js index a5dee646a..c3b1f2f4a 100644 --- a/src/components/app/DetailsPanel.js +++ b/src/components/app/DetailsPanel.js @@ -2,6 +2,7 @@ import cx from 'classnames' import PropTypes from 'prop-types' import React from 'react' import { useSelector } from 'react-redux' +import FeatureProfile from '../feature/FeatureProfile.js' import Interpretations from '../interpretations/Interpretations.js' import OrgUnitProfile from '../orgunits/OrgUnitProfile.js' import styles from './styles/DetailsPanel.module.css' @@ -9,14 +10,27 @@ import styles from './styles/DetailsPanel.module.css' const DetailsPanel = ({ interpretationsRenderCount }) => { const detailsPanelOpen = useSelector((state) => state.ui.rightPanelOpen) const viewOrgUnitProfile = useSelector((state) => state.orgUnitProfile) + const viewFeatureProfile = useSelector((state) => !!state.featureProfile) const interpretationId = useSelector((state) => state.interpretation?.id) const getContent = () => { - if (interpretationId || (detailsPanelOpen && !viewOrgUnitProfile)) { + if ( + interpretationId || + (detailsPanelOpen && !viewOrgUnitProfile && !viewFeatureProfile) + ) { return } - return detailsPanelOpen ? : null + if (detailsPanelOpen) { + if (viewOrgUnitProfile) { + return + } + if (viewFeatureProfile) { + return + } + } + + return null } return ( diff --git a/src/components/app/FileMenu.js b/src/components/app/FileMenu.js index 77f79eb80..f7f7c0641 100644 --- a/src/components/app/FileMenu.js +++ b/src/components/app/FileMenu.js @@ -88,10 +88,6 @@ const FileMenu = ({ onFileMenuAction }) => { defaultBasemapId: defaultBasemap, }) - if (config.mapViews) { - config.mapViews.forEach((view) => delete view.id) - } - await putMap({ id: map.id, data: config, @@ -115,10 +111,6 @@ const FileMenu = ({ onFileMenuAction }) => { delete data.id - if (data.mapViews) { - data.mapViews.forEach((view) => delete view.id) - } - const res = await postMap({ data }) if (res.status === 'OK') { diff --git a/src/components/app/__tests__/Detailspanel.spec.js b/src/components/app/__tests__/Detailspanel.spec.js index 6d6110eef..2ad70e5cb 100644 --- a/src/components/app/__tests__/Detailspanel.spec.js +++ b/src/components/app/__tests__/Detailspanel.spec.js @@ -22,121 +22,113 @@ jest.mock( } ) -describe('DetailsPanel', () => { - test('renders InterpretationsPanel when has interpretationId, has orgUnitProfile and panel is open', () => { - const store = { - interpretation: { id: 'abc123' }, - orgUnitProfile: 'xyzpdq', - ui: { rightPanelOpen: true }, - } - - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) - test('renders InterpretationsPanel when has interpretationId, has orgUnitProfile and panel is closed', () => { - const store = { - interpretation: { id: 'abc123' }, - orgUnitProfile: 'xyzpdq', - ui: { rightPanelOpen: false }, +jest.mock( + '../../feature/FeatureProfile.js', + () => + function MockFeatureProfile() { + return
Feature Profile
} +) - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) - test('renders InterpretationsPanel when has interpretationId, no orgUnitProfile and panel is open', () => { - const store = { - interpretation: { id: 'abc123' }, +const interpretation = { id: 'myinterpretation' } +const orgUnitProfile = 'myorgunit' +const featureProfile = 'myfeature' +const uiWithPanelOpen = { rightPanelOpen: true } +const uiWithPanelClosed = { rightPanelOpen: false } + +const testCases = [ + { + store: { + interpretation, + orgUnitProfile, + featureProfile, + ui: uiWithPanelOpen, + }, + expected: 'Interpretations', + }, + { + store: { + interpretation, + orgUnitProfile, + featureProfile, + ui: uiWithPanelClosed, + }, + expected: 'Interpretations', + }, + { + store: { + interpretation, orgUnitProfile: null, - ui: { rightPanelOpen: true }, - } - - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) - - test('renders InterpretationsPanel when has interpretationId, no orgUnitProfile and panel is closed', () => { - const store = { - interpretation: { id: 'abc123' }, + featureProfile: null, + ui: uiWithPanelOpen, + }, + expected: 'Interpretations', + }, + { + store: { + interpretation, orgUnitProfile: null, - ui: { rightPanelOpen: false }, - } - - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) - - test('renders OrgUnitProfile when no interpretationId, has orgUnitProfile and panel is open', () => { - const store = { + featureProfile: null, + ui: uiWithPanelClosed, + }, + expected: 'Interpretations', + }, + { + store: { interpretation: {}, - orgUnitProfile: 'xyzpdq', - ui: { rightPanelOpen: true }, - } - - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) - - test('renders null when no interpretationId, has orgUnitProfile, and panel closed', () => { - const store = { + orgUnitProfile: null, + featureProfile: null, + ui: uiWithPanelOpen, + }, + expected: 'Interpretations', + }, + { + store: { interpretation: {}, - orgUnitProfile: 'xyzpdq', - ui: { rightPanelOpen: false }, - } - - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) - test('renders InterpretationsPanel when no interpretationId, no orgUnitProfile and panel open', () => { - const store = { + orgUnitProfile, + featureProfile, + ui: uiWithPanelOpen, + }, + expected: 'OrgUnitProfile', + }, + { + store: { interpretation: {}, orgUnitProfile: null, - ui: { rightPanelOpen: true }, - } - - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() - }) - - test('renders null when no interpretationId, no orgUnitProfile, and panel closed', () => { - const store = { + featureProfile, + ui: uiWithPanelOpen, + }, + expected: 'FeatureProfile', + }, + { + store: { + interpretation: {}, + orgUnitProfile, + featureProfile: null, + ui: uiWithPanelClosed, + }, + expected: 'null', + }, + { + store: { interpretation: {}, orgUnitProfile: null, - ui: { rightPanelOpen: false }, - } + featureProfile: null, + ui: uiWithPanelClosed, + }, + expected: 'null', + }, +] - const { container } = render( - - - - ) - expect(container).toMatchSnapshot() +describe('DetailsPanel', () => { + testCases.forEach(({ store, expected }) => { + test(`renders ${expected} when interpretation=${store.interpretation.id}, orgUnitProfile=${store.orgUnitProfile}, featureProfile=${store.featureProfile} and panelOpen=${store.ui.rightPanelOpen}`, () => { + const { container } = render( + + + + ) + expect(container).toMatchSnapshot() + }) }) }) diff --git a/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap b/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap index b45d254ce..8b550fdfd 100644 --- a/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap +++ b/src/components/app/__tests__/__snapshots__/Detailspanel.spec.js.snap @@ -1,6 +1,18 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP -exports[`DetailsPanel renders InterpretationsPanel when has interpretationId, has orgUnitProfile and panel is closed 1`] = ` +exports[`DetailsPanel renders FeatureProfile when interpretation=undefined, orgUnitProfile=null, featureProfile=myfeature and panelOpen=true 1`] = ` +
+
+
+ Feature Profile +
+
+
+`; + +exports[`DetailsPanel renders Interpretations when interpretation=myinterpretation, orgUnitProfile=myorgunit, featureProfile=myfeature and panelOpen=false 1`] = `