From 0b936aae283b13f899fa9ee657f9409709a0261b Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Sat, 19 Nov 2022 09:45:50 +0100 Subject: [PATCH] Refactor redux store --- package-lock.json | 194 +----------------- package.json | 2 +- .../assets/js/actions/configActions.js | 6 + .../assets/js/actions/elementActions.js | 15 ++ .../assets/js/actions/questionsActions.js | 15 -- rdmo/management/assets/js/api/QuestionsApi.js | 13 ++ rdmo/management/assets/js/components/Link.js | 23 +++ rdmo/management/assets/js/containers/Main.js | 28 ++- .../assets/js/containers/Sidebar.js | 54 ++++- rdmo/management/assets/js/management.js | 10 +- .../assets/js/reducers/configReducer.js | 18 ++ .../assets/js/reducers/elementsReducer.js | 23 +++ .../assets/js/reducers/questionsReducer.js | 15 -- .../assets/js/reducers/rootReducer.js | 11 +- .../assets/js/store/addEventListener.js | 9 + .../assets/js/store/configureStore.js | 17 ++ rdmo/management/assets/js/utils/locations.js | 29 +++ rdmo/management/assets/scss/management.scss | 6 +- rdmo/management/urls/__init__.py | 6 +- 19 files changed, 251 insertions(+), 243 deletions(-) create mode 100644 rdmo/management/assets/js/actions/configActions.js create mode 100644 rdmo/management/assets/js/actions/elementActions.js delete mode 100644 rdmo/management/assets/js/actions/questionsActions.js create mode 100644 rdmo/management/assets/js/components/Link.js create mode 100644 rdmo/management/assets/js/reducers/configReducer.js create mode 100644 rdmo/management/assets/js/reducers/elementsReducer.js delete mode 100644 rdmo/management/assets/js/reducers/questionsReducer.js create mode 100644 rdmo/management/assets/js/store/addEventListener.js create mode 100644 rdmo/management/assets/js/store/configureStore.js create mode 100644 rdmo/management/assets/js/utils/locations.js diff --git a/package-lock.json b/package-lock.json index 21eb2bbe5..ab8c80058 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,13 +18,13 @@ "jquery": "^3.6.0", "js-cookie": "^2.2.1", "local-storage": "^2.0.0", + "lodash": "^4.17.21", "popper.js": "^1.16.1", "prop-types": "^15.7.2", "react": "^17.0.2", "react-bootstrap": "^1.5.2", "react-dom": "^17.0.2", "react-redux": "^7.2.4", - "react-router-dom": "^5.2.0", "redux": "^4.1.1", "redux-thunk": "^2.3.0" }, @@ -3335,19 +3335,6 @@ "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "dependencies": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -3522,11 +3509,6 @@ "url": "https://github.com/sponsors/sindresorhus" } }, - "node_modules/isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -3689,6 +3671,11 @@ "node": ">=8" } }, + "node_modules/lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "node_modules/lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -3804,19 +3791,6 @@ "node": ">=6" } }, - "node_modules/mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "dependencies": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - }, - "peerDependencies": { - "prop-types": "^15.0.0", - "react": "^0.14.0 || ^15.0.0 || ^16.0.0 || ^17.0.0" - } - }, "node_modules/mini-css-extract-plugin": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz", @@ -4057,14 +4031,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "node_modules/path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "dependencies": { - "isarray": "0.0.1" - } - }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -4396,43 +4362,6 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" }, - "node_modules/react-router": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.1.tgz", - "integrity": "sha512-v+zwjqb7bakqgF+wMVKlAPTca/cEmPOvQ9zt7gpSNyPXau1+0qvuYZ5BWzzNDP1y6s15zDwgb9rPN63+SIniRQ==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, - "node_modules/react-router-dom": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.1.tgz", - "integrity": "sha512-f0pj/gMAbv9e8gahTmCEY20oFhxhrmHwYeIwH5EO5xu0qme+wXtsdB8YfUOAZzUz4VaXmb58m3ceiLtjMhqYmQ==", - "dependencies": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.1", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - }, - "peerDependencies": { - "react": ">=15" - } - }, "node_modules/react-transition-group": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", @@ -4602,11 +4531,6 @@ "node": ">=8" } }, - "node_modules/resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -4954,16 +4878,6 @@ "node": ">= 8" } }, - "node_modules/tiny-invariant": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", - "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" - }, - "node_modules/tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "node_modules/to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -5063,11 +4977,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "node_modules/value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, "node_modules/warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", @@ -7682,19 +7591,6 @@ "integrity": "sha512-l3LCuF6MgDNwTDKkdYGEihYjt5pRPbEg46rtlmnSPlUbgmB8LOIrKJbYYFBSbnPaJexMKtiPO8hmeRjRz2Td+A==", "dev": true }, - "history": { - "version": "4.10.1", - "resolved": "https://registry.npmjs.org/history/-/history-4.10.1.tgz", - "integrity": "sha512-36nwAD620w12kuzPAsyINPWJqlNbij+hpK1k9XRloDtym8mxzGYl2c17LnV6IAGB2Dmg4tEa7G7DlawS0+qjew==", - "requires": { - "@babel/runtime": "^7.1.2", - "loose-envify": "^1.2.0", - "resolve-pathname": "^3.0.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0", - "value-equal": "^1.0.1" - } - }, "hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -7822,11 +7718,6 @@ "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true }, - "isarray": { - "version": "0.0.1", - "resolved": "https://registry.npmjs.org/isarray/-/isarray-0.0.1.tgz", - "integrity": "sha1-ihis/Kmo9Bd+Cav8YDiTmwXR7t8=" - }, "isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", @@ -7949,6 +7840,11 @@ "p-locate": "^4.1.0" } }, + "lodash": { + "version": "4.17.21", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz", + "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==" + }, "lodash.debounce": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/lodash.debounce/-/lodash.debounce-4.0.8.tgz", @@ -8039,15 +7935,6 @@ "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true }, - "mini-create-react-context": { - "version": "0.4.1", - "resolved": "https://registry.npmjs.org/mini-create-react-context/-/mini-create-react-context-0.4.1.tgz", - "integrity": "sha512-YWCYEmd5CQeHGSAKrYvXgmzzkrvssZcuuQDDeqkT+PziKGMgE+0MCCtcKbROzocGBG1meBLl2FotlRwf4gAzbQ==", - "requires": { - "@babel/runtime": "^7.12.1", - "tiny-warning": "^1.0.3" - } - }, "mini-css-extract-plugin": { "version": "1.6.2", "resolved": "https://registry.npmjs.org/mini-css-extract-plugin/-/mini-css-extract-plugin-1.6.2.tgz", @@ -8219,14 +8106,6 @@ "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "dev": true }, - "path-to-regexp": { - "version": "1.8.0", - "resolved": "https://registry.npmjs.org/path-to-regexp/-/path-to-regexp-1.8.0.tgz", - "integrity": "sha512-n43JRhlUKUAlibEJhPeir1ncUID16QnEjNpwzNdO3Lm4ywrBpBZ5oLD0I6br9evr1Y9JTqwRtAh7JLoOzAQdVA==", - "requires": { - "isarray": "0.0.1" - } - }, "path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -8456,37 +8335,6 @@ } } }, - "react-router": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/react-router/-/react-router-5.3.1.tgz", - "integrity": "sha512-v+zwjqb7bakqgF+wMVKlAPTca/cEmPOvQ9zt7gpSNyPXau1+0qvuYZ5BWzzNDP1y6s15zDwgb9rPN63+SIniRQ==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "hoist-non-react-statics": "^3.1.0", - "loose-envify": "^1.3.1", - "mini-create-react-context": "^0.4.0", - "path-to-regexp": "^1.7.0", - "prop-types": "^15.6.2", - "react-is": "^16.6.0", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - } - }, - "react-router-dom": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-5.3.1.tgz", - "integrity": "sha512-f0pj/gMAbv9e8gahTmCEY20oFhxhrmHwYeIwH5EO5xu0qme+wXtsdB8YfUOAZzUz4VaXmb58m3ceiLtjMhqYmQ==", - "requires": { - "@babel/runtime": "^7.12.13", - "history": "^4.9.0", - "loose-envify": "^1.3.1", - "prop-types": "^15.6.2", - "react-router": "5.3.1", - "tiny-invariant": "^1.0.2", - "tiny-warning": "^1.0.0" - } - }, "react-transition-group": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.2.tgz", @@ -8622,11 +8470,6 @@ "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true }, - "resolve-pathname": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/resolve-pathname/-/resolve-pathname-3.0.0.tgz", - "integrity": "sha512-C7rARubxI8bXFNB/hqcp/4iUeIXJhJZvFPFPiSPRnhU5UPxzMFIl+2E6yY6c4k9giDJAhtV+enfA+G89N6Csng==" - }, "reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", @@ -8850,16 +8693,6 @@ } } }, - "tiny-invariant": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/tiny-invariant/-/tiny-invariant-1.2.0.tgz", - "integrity": "sha512-1Uhn/aqw5C6RI4KejVeTg6mIS7IqxnLJ8Mv2tV5rTc0qWobay7pDUz6Wi392Cnc8ak1H0F2cjoRzb2/AW4+Fvg==" - }, - "tiny-warning": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/tiny-warning/-/tiny-warning-1.0.3.tgz", - "integrity": "sha512-lBN9zLN/oAf68o3zNXYrdCt1kP8WsiGW8Oo2ka41b2IM5JL/S1CTyX1rW0mb/zSuJun0ZUrDxx4sqvYS2FWzPA==" - }, "to-fast-properties": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz", @@ -8938,11 +8771,6 @@ "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", "dev": true }, - "value-equal": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/value-equal/-/value-equal-1.0.1.tgz", - "integrity": "sha512-NOJ6JZCAWr0zlxZt+xqCHNTEKOsrks2HQd4MqhP1qy4z1SkbEP467eNx6TgDKXMvUOb+OENfJCZwM+16n7fRfw==" - }, "warning": { "version": "4.0.3", "resolved": "https://registry.npmjs.org/warning/-/warning-4.0.3.tgz", diff --git a/package.json b/package.json index 92865c388..c3c9f2abc 100644 --- a/package.json +++ b/package.json @@ -22,13 +22,13 @@ "jquery": "^3.6.0", "js-cookie": "^2.2.1", "local-storage": "^2.0.0", + "lodash": "^4.17.21", "popper.js": "^1.16.1", "prop-types": "^15.7.2", "react": "^17.0.2", "react-bootstrap": "^1.5.2", "react-dom": "^17.0.2", "react-redux": "^7.2.4", - "react-router-dom": "^5.2.0", "redux": "^4.1.1", "redux-thunk": "^2.3.0" }, diff --git a/rdmo/management/assets/js/actions/configActions.js b/rdmo/management/assets/js/actions/configActions.js new file mode 100644 index 000000000..7d072aab3 --- /dev/null +++ b/rdmo/management/assets/js/actions/configActions.js @@ -0,0 +1,6 @@ +export function updateConfig(config) { + return {type: 'config/updateConfig', config} +} +export function updateConfigAndLocation(config) { + return {type: 'config/updateConfigAndLocation', config} +} diff --git a/rdmo/management/assets/js/actions/elementActions.js b/rdmo/management/assets/js/actions/elementActions.js new file mode 100644 index 000000000..5e0718cc6 --- /dev/null +++ b/rdmo/management/assets/js/actions/elementActions.js @@ -0,0 +1,15 @@ +import QuestionsApi from '../api/QuestionsApi' + +export function fetchCatalogsSuccess(catalogs) { + return {type: 'elements/fetchCatalogsSuccess', catalogs} +} + +export function fetchCatalogs() { + return function(dispatch) { + return QuestionsApi.fetchCatalogs().then(catalogs => { + dispatch(fetchCatalogsSuccess(catalogs)) + }).catch(error => { + throw(error) + }) + } +} diff --git a/rdmo/management/assets/js/actions/questionsActions.js b/rdmo/management/assets/js/actions/questionsActions.js deleted file mode 100644 index 374f7552e..000000000 --- a/rdmo/management/assets/js/actions/questionsActions.js +++ /dev/null @@ -1,15 +0,0 @@ -import QuestionsApi from '../api/QuestionsApi' - -export function fetchCatalogSuccess(catalog) { - return {type: 'questions/fetchCatalogSuccess', catalog} -} - -export function fetchCatalog(id) { - return function(dispatch) { - return QuestionsApi.fetchCatalog(id, true).then(catalog => { - dispatch(fetchCatalogSuccess(catalog)) - }).catch(error => { - throw(error) - }) - } -} diff --git a/rdmo/management/assets/js/api/QuestionsApi.js b/rdmo/management/assets/js/api/QuestionsApi.js index ce90dfc7e..338bd5d89 100644 --- a/rdmo/management/assets/js/api/QuestionsApi.js +++ b/rdmo/management/assets/js/api/QuestionsApi.js @@ -1,5 +1,18 @@ class QuestionsApi { + static fetchCatalogs(index=false) { + let url = `/api/v1/questions/catalogs/` + if (index) { + url += 'index/' + } + + return fetch(url).then(response => { + return response.json() + }).catch(error => { + return error + }); + } + static fetchCatalog(id, nested=false) { let url = `/api/v1/questions/catalogs/${id}/` if (nested) { diff --git a/rdmo/management/assets/js/components/Link.js b/rdmo/management/assets/js/components/Link.js new file mode 100644 index 000000000..b79399aca --- /dev/null +++ b/rdmo/management/assets/js/components/Link.js @@ -0,0 +1,23 @@ +import React, { Component} from 'react' +import PropTypes from 'prop-types' + +const Link = ({ resource, id, onClick, children }) => { + const handleClick = (event) => { + event.preventDefault() + onClick({ resource, id }) + } + + return ( + handleClick(event)}> + {children} + + ) +} + +Link.propTypes = { + onClick: PropTypes.func.isRequired, + resource: PropTypes.string, + id: PropTypes.number, +} + +export default Link diff --git a/rdmo/management/assets/js/containers/Main.js b/rdmo/management/assets/js/containers/Main.js index ddcfe64e1..122bd6edc 100644 --- a/rdmo/management/assets/js/containers/Main.js +++ b/rdmo/management/assets/js/containers/Main.js @@ -2,32 +2,42 @@ import React, { Component} from 'react' import PropTypes from 'prop-types' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' -import * as questionsActions from '../actions/questionsActions' +import * as configActions from '../actions/configActions' +import * as elementActions from '../actions/elementActions' import Catalog from '../components/Catalog' -const Main = ({ questions, questionsActions }) => { - if (questions.catalog !== null) { - return - } else { + +class Main extends Component { + + constructor(props) { + super(props) + } + + render() { return null } + } Main.propTypes = { - questions: PropTypes.object.isRequired, - questionsActions: PropTypes.object.isRequired + config: PropTypes.object.isRequired, + elements: PropTypes.object.isRequired, + configActions: PropTypes.object.isRequired, + elementActions: PropTypes.object.isRequired } function mapStateToProps(state, props) { return { - questions: state.questions + config: state.config, + elements: state.elements } } function mapDispatchToProps(dispatch) { return { - questionsActions: bindActionCreators(questionsActions, dispatch) + configActions: bindActionCreators(configActions, dispatch), + elementActions: bindActionCreators(elementActions, dispatch) } } diff --git a/rdmo/management/assets/js/containers/Sidebar.js b/rdmo/management/assets/js/containers/Sidebar.js index 5d31d0807..f01394186 100644 --- a/rdmo/management/assets/js/containers/Sidebar.js +++ b/rdmo/management/assets/js/containers/Sidebar.js @@ -2,25 +2,65 @@ import React, { Component} from 'react' import PropTypes from 'prop-types' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' -import * as questionsActions from '../actions/questionsActions' +import * as configActions from '../actions/configActions' +import * as elementActions from '../actions/elementActions' +import Link from '../components/Link' -const Sidebar = ({ questionsActions }) => { - return +class Sidebar extends Component { + + constructor(props) { + super(props) + } + + render() { + const { config, elements, configActions, elementActions } = this.props + + return ( +
+

+ {config.resource} +

+
    +
  • + Catalogs +
  • +
  • + Sections +
  • +
  • + Pages +
  • +
  • + Questionsets +
  • +
  • + Questions +
  • +
+
+ ) + } } Sidebar.propTypes = { - questionsActions: PropTypes.object.isRequired + config: PropTypes.object.isRequired, + elements: PropTypes.object.isRequired, + configActions: PropTypes.object.isRequired, + elementActions: PropTypes.object.isRequired } function mapStateToProps(state, props) { - return {} + return { + config: state.config, + elements: state.elements + } } function mapDispatchToProps(dispatch) { return { - questionsActions: bindActionCreators(questionsActions, dispatch) + configActions: bindActionCreators(configActions, dispatch), + elementActions: bindActionCreators(elementActions, dispatch) } } diff --git a/rdmo/management/assets/js/management.js b/rdmo/management/assets/js/management.js index d4310aa1d..c61bebf52 100644 --- a/rdmo/management/assets/js/management.js +++ b/rdmo/management/assets/js/management.js @@ -1,16 +1,16 @@ import React from "react" import ReactDOM from "react-dom" -import { createStore, applyMiddleware } from 'redux' import { Provider } from 'react-redux' -import thunk from 'redux-thunk' -import ls from 'local-storage' -import rootReducer from './reducers/rootReducer' +import configureStore from './store/configureStore' +import addEventListener from './store/addEventListener' import Main from './containers/Main' import Sidebar from './containers/Sidebar' -const store = createStore(rootReducer, applyMiddleware(thunk)) +const store = configureStore() + +addEventListener(store.dispatch, store.getState) ReactDOM.render(
, document.getElementById('main')) ReactDOM.render(, document.getElementById('sidebar')) diff --git a/rdmo/management/assets/js/reducers/configReducer.js b/rdmo/management/assets/js/reducers/configReducer.js new file mode 100644 index 000000000..0b5a6d2ce --- /dev/null +++ b/rdmo/management/assets/js/reducers/configReducer.js @@ -0,0 +1,18 @@ +import { parseLocation, updateLocation } from '../utils/locations' + +const basePath = '/management/' +const initialState = parseLocation(basePath, window.location.pathname) + +export default function configReducer(state = initialState, action) { + + switch(action.type) { + case 'config/updateConfig': + return Object.assign({}, state, action.config) + case 'config/updateConfigAndLocation': + updateLocation(basePath, action.config) + return Object.assign({}, state, action.config) + default: + return state + } + +} diff --git a/rdmo/management/assets/js/reducers/elementsReducer.js b/rdmo/management/assets/js/reducers/elementsReducer.js new file mode 100644 index 000000000..85a56c7f4 --- /dev/null +++ b/rdmo/management/assets/js/reducers/elementsReducer.js @@ -0,0 +1,23 @@ +const initialState = { + catalogs: [], + sections: [], + pages: [], + questionsets: [], + questions: [], + optionsets: [], + options: [], + conditions: [], + tasks: [], + views: [] +} + +export default function elementsReducer(state = initialState, action) { + + switch(action.type) { + case 'elemets/fetchCatalogs': + return Object.assign({}, state, { catalogs: action.catalogs }) + default: + return state + } + +} diff --git a/rdmo/management/assets/js/reducers/questionsReducer.js b/rdmo/management/assets/js/reducers/questionsReducer.js deleted file mode 100644 index 34d677dfe..000000000 --- a/rdmo/management/assets/js/reducers/questionsReducer.js +++ /dev/null @@ -1,15 +0,0 @@ -const initialState = { - catalog: null, - catalogs: [] -} - -export default function catalogReducer(state = initialState, action) { - - switch(action.type) { - case 'questions/fetchCatalogSuccess': - return Object.assign({}, state, { catalog: action.catalog }) - default: - return state - } - -} diff --git a/rdmo/management/assets/js/reducers/rootReducer.js b/rdmo/management/assets/js/reducers/rootReducer.js index b87b8ec38..83f1812d0 100644 --- a/rdmo/management/assets/js/reducers/rootReducer.js +++ b/rdmo/management/assets/js/reducers/rootReducer.js @@ -1,8 +1,11 @@ import { combineReducers } from 'redux' -import questionsReducer from './questionsReducer' -const rootReducer = combineReducers({ - questions: questionsReducer +import configReducer from './configReducer' +import elementsReducer from './elementsReducer' + +const createRootReducer = () => combineReducers({ + config: configReducer, + elements: elementsReducer }) -export default rootReducer +export default createRootReducer diff --git a/rdmo/management/assets/js/store/addEventListener.js b/rdmo/management/assets/js/store/addEventListener.js new file mode 100644 index 000000000..3caac1031 --- /dev/null +++ b/rdmo/management/assets/js/store/addEventListener.js @@ -0,0 +1,9 @@ +import { parseLocation } from '../utils/locations' +import * as configActions from '../actions/configActions' + +export default function addEventListener(dispatch, getState) { + window.addEventListener('popstate', (event) => { + const config = parseLocation('/management/', event.target.location.pathname) + dispatch(configActions.updateConfig(config)) + }); +} diff --git a/rdmo/management/assets/js/store/configureStore.js b/rdmo/management/assets/js/store/configureStore.js new file mode 100644 index 000000000..be7848bde --- /dev/null +++ b/rdmo/management/assets/js/store/configureStore.js @@ -0,0 +1,17 @@ +import { applyMiddleware, compose, createStore } from 'redux' +import thunk from 'redux-thunk' + +import createRootReducer from '../reducers/rootReducer' + +export default function configureStore() { + const store = createStore( + createRootReducer(), + compose( + applyMiddleware( + thunk + ), + ), + ) + + return store +} diff --git a/rdmo/management/assets/js/utils/locations.js b/rdmo/management/assets/js/utils/locations.js new file mode 100644 index 000000000..0e78fbe58 --- /dev/null +++ b/rdmo/management/assets/js/utils/locations.js @@ -0,0 +1,29 @@ +import _ from 'lodash' + +const parseLocation = (basePath, pathname) => { + const path = pathname.replace(basePath, '') + const tokens = _.trim(path, '/').split('/') + + const config = {} + if (tokens.length >= 1) { + config.resource = tokens[0] + } + if (tokens.length >= 2) { + config.id = tokens[1] + } + + return config +} + +const updateLocation = (basePath, { resource, id }) => { + let pathname = basePath + if (!_.isUndefined(resource)) { + pathname += resource + '/' + } + if (!_.isUndefined(id)) { + pathname += id + '/' + } + history.pushState(null, null, pathname); +} + +export { parseLocation, updateLocation } diff --git a/rdmo/management/assets/scss/management.scss b/rdmo/management/assets/scss/management.scss index 987fee2b9..2094ce77d 100644 --- a/rdmo/management/assets/scss/management.scss +++ b/rdmo/management/assets/scss/management.scss @@ -1,3 +1,7 @@ body { -} \ No newline at end of file +} + +.mb-20 { + margin-bottom: 20px; +} diff --git a/rdmo/management/urls/__init__.py b/rdmo/management/urls/__init__.py index 65c364453..e63181ac4 100644 --- a/rdmo/management/urls/__init__.py +++ b/rdmo/management/urls/__init__.py @@ -1,9 +1,9 @@ -from django.urls import path +from django.urls import path, re_path from ..views import ManagementView, ImportView, UploadView urlpatterns = [ - path('', ManagementView.as_view(), name='management'), path('upload/', UploadView.as_view(), name='upload'), - path('import/', ImportView.as_view(), name='import') + path('import/', ImportView.as_view(), name='import'), + re_path('', ManagementView.as_view(), name='management') ]