From eec363fa3a07b675afe6eae7ebffb11dda1b0ed2 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 12 Mar 2024 18:11:11 +0100 Subject: [PATCH 01/72] Add project_interview view and refactor webpack config --- rdmo/projects/assets/js/interview.js | 1 + rdmo/projects/assets/scss/interview.scss | 3 ++ .../templates/projects/project_interview.html | 32 +++++++++++++++++++ rdmo/projects/urls/__init__.py | 3 ++ rdmo/projects/views/__init__.py | 1 + rdmo/projects/views/project.py | 16 ++++++++++ 6 files changed, 56 insertions(+) create mode 100644 rdmo/projects/assets/js/interview.js create mode 100644 rdmo/projects/assets/scss/interview.scss create mode 100644 rdmo/projects/templates/projects/project_interview.html diff --git a/rdmo/projects/assets/js/interview.js b/rdmo/projects/assets/js/interview.js new file mode 100644 index 0000000000..f30bda44bb --- /dev/null +++ b/rdmo/projects/assets/js/interview.js @@ -0,0 +1 @@ +console.log('interview') diff --git a/rdmo/projects/assets/scss/interview.scss b/rdmo/projects/assets/scss/interview.scss new file mode 100644 index 0000000000..0ae12b5d5c --- /dev/null +++ b/rdmo/projects/assets/scss/interview.scss @@ -0,0 +1,3 @@ +.interview { + +} diff --git a/rdmo/projects/templates/projects/project_interview.html b/rdmo/projects/templates/projects/project_interview.html new file mode 100644 index 0000000000..761136204d --- /dev/null +++ b/rdmo/projects/templates/projects/project_interview.html @@ -0,0 +1,32 @@ +{% extends 'core/page.html' %} +{% load static %} +{% load i18n %} +{% load core_tags %} + +{% block vendor %} +{% endblock %} + +{% block css %} + + +{% endblock %} + +{% block js %} + + + +{% endblock %} + +{% block sidebar %} + + + +{% endblock %} + +{% block page %} + +
+ Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. +
+ +{% endblock %} diff --git a/rdmo/projects/urls/__init__.py b/rdmo/projects/urls/__init__.py index b7cc6d81f9..7fa4a27705 100644 --- a/rdmo/projects/urls/__init__.py +++ b/rdmo/projects/urls/__init__.py @@ -22,6 +22,7 @@ ProjectDetailView, ProjectErrorView, ProjectExportView, + ProjectInterviewView, ProjectJoinView, ProjectLeaveView, ProjectQuestionsView, @@ -138,6 +139,8 @@ re_path(r'^(?P[0-9]+)/questions/', ProjectQuestionsView.as_view(), name='project_questions'), + re_path(r'^(?P[0-9]+)/interview/', + ProjectInterviewView.as_view(), name='project_interview'), re_path(r'^(?P[0-9]+)/error/', ProjectErrorView.as_view(), name='project_error'), ] diff --git a/rdmo/projects/views/__init__.py b/rdmo/projects/views/__init__.py index a6899c8671..7cee69be9f 100644 --- a/rdmo/projects/views/__init__.py +++ b/rdmo/projects/views/__init__.py @@ -8,6 +8,7 @@ ProjectDetailView, ProjectErrorView, ProjectExportView, + ProjectInterviewView, ProjectJoinView, ProjectLeaveView, ProjectQuestionsView, diff --git a/rdmo/projects/views/project.py b/rdmo/projects/views/project.py index 88fc5fc99a..9760fbd3b6 100644 --- a/rdmo/projects/views/project.py +++ b/rdmo/projects/views/project.py @@ -203,6 +203,22 @@ def get(self, request, *args, **kwargs): return self.render_to_response(context) +class ProjectInterviewView(ObjectPermissionMixin, DetailView): + model = Project + queryset = Project.objects.all() + permission_required = 'projects.view_project_object' + template_name = 'projects/project_interview.html' + + @method_decorator(ensure_csrf_cookie) + def get(self, request, *args, **kwargs): + self.object = self.get_object() + + if self.object.catalog is None: + return redirect('project_error', pk=self.object.pk) + else: + return super().get(request, *args, **kwargs) + + class ProjectErrorView(ObjectPermissionMixin, DetailView): model = Project queryset = Project.objects.all() From 1c6489caaf4bcc8be076485557e5b2a0d145e050 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 14 Mar 2024 12:48:46 +0100 Subject: [PATCH 02/72] Add lint script to package.json --- package.json | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/package.json b/package.json index 066ec6aa30..27606a11b2 100644 --- a/package.json +++ b/package.json @@ -4,7 +4,8 @@ "build:dist": "webpack --config webpack.config.js --mode production --env ignore-perf --fail-on-warnings", "build:prod": "webpack --config webpack.config.js --mode production", "build": "webpack --config webpack.config.js --mode development", - "watch": "webpack --config webpack.config.js --mode development --watch" + "watch": "webpack --config webpack.config.js --mode development --watch", + "lint": "eslint --ext .js rdmo/" }, "author": "RDMO Arbeitsgemeinschaft ", "license": "Apache-2.0", From 4a25cfbf0712b752155f293c11ea2df7859d1188 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 14 Mar 2024 12:49:25 +0100 Subject: [PATCH 03/72] Move Pending container to core/assets --- .../assets/js/containers/Pending.js | 23 ------------------- rdmo/management/assets/js/management.js | 3 ++- 2 files changed, 2 insertions(+), 24 deletions(-) delete mode 100644 rdmo/management/assets/js/containers/Pending.js diff --git a/rdmo/management/assets/js/containers/Pending.js b/rdmo/management/assets/js/containers/Pending.js deleted file mode 100644 index 7b421c49df..0000000000 --- a/rdmo/management/assets/js/containers/Pending.js +++ /dev/null @@ -1,23 +0,0 @@ -import React from 'react' -import PropTypes from 'prop-types' -import { connect } from 'react-redux' - -const Pending = ({ config }) => { - if (config.pending) { - return - } else { - return null - } -} - -Pending.propTypes = { - config: PropTypes.object.isRequired, -} - -function mapStateToProps(state) { - return { - config: state.config, - } -} - -export default connect(mapStateToProps)(Pending) diff --git a/rdmo/management/assets/js/management.js b/rdmo/management/assets/js/management.js index 55b37879ed..122f043cd2 100644 --- a/rdmo/management/assets/js/management.js +++ b/rdmo/management/assets/js/management.js @@ -7,9 +7,10 @@ import configureStore from './store/configureStore' import { DndProvider } from 'react-dnd' import { HTML5Backend } from 'react-dnd-html5-backend' +import Pending from '../../../core/assets/js/containers/Pending' + import Main from './containers/Main' import Sidebar from './containers/Sidebar' -import Pending from './containers/Pending' const store = configureStore() From 40cd8d80e02c9188829a1d3e4fe81e0e64403187 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Thu, 14 Mar 2024 12:50:29 +0100 Subject: [PATCH 04/72] Add react/redux setup for interview --- rdmo/projects/assets/js/interview.js | 36 +++++++++++++++- .../js/interview/actions/configActions.js | 23 ++++++++++ .../assets/js/interview/actions/types.js | 3 ++ .../assets/js/interview/api/ProjectsApi.js | 0 .../assets/js/interview/containers/Main.js | 38 ++++++++++++++++ .../assets/js/interview/containers/Sidebar.js | 38 ++++++++++++++++ .../js/interview/reducers/configReducer.js | 29 +++++++++++++ .../js/interview/reducers/rootReducer.js | 9 ++++ .../js/interview/store/configureStore.js | 43 +++++++++++++++++++ .../templates/projects/project_interview.html | 4 +- 10 files changed, 219 insertions(+), 4 deletions(-) create mode 100644 rdmo/projects/assets/js/interview/actions/configActions.js create mode 100644 rdmo/projects/assets/js/interview/actions/types.js create mode 100644 rdmo/projects/assets/js/interview/api/ProjectsApi.js create mode 100644 rdmo/projects/assets/js/interview/containers/Main.js create mode 100644 rdmo/projects/assets/js/interview/containers/Sidebar.js create mode 100644 rdmo/projects/assets/js/interview/reducers/configReducer.js create mode 100644 rdmo/projects/assets/js/interview/reducers/rootReducer.js create mode 100644 rdmo/projects/assets/js/interview/store/configureStore.js diff --git a/rdmo/projects/assets/js/interview.js b/rdmo/projects/assets/js/interview.js index f30bda44bb..97435a43f5 100644 --- a/rdmo/projects/assets/js/interview.js +++ b/rdmo/projects/assets/js/interview.js @@ -1 +1,35 @@ -console.log('interview') +import React from 'react' +import { createRoot } from 'react-dom/client' +import { Provider } from 'react-redux' + +import configureStore from './interview/store/configureStore' + +import { DndProvider } from 'react-dnd' +import { HTML5Backend } from 'react-dnd-html5-backend' + +import Pending from '../../../core/assets/js/containers/Pending' + +import Main from './interview/containers/Main' +import Sidebar from './interview/containers/Sidebar' + +const store = configureStore() + +createRoot(document.getElementById('main')).render( + + +
+ + +) + +createRoot(document.getElementById('sidebar')).render( + + + +) + +createRoot(document.getElementById('pending')).render( + + + +) diff --git a/rdmo/projects/assets/js/interview/actions/configActions.js b/rdmo/projects/assets/js/interview/actions/configActions.js new file mode 100644 index 0000000000..1e19b0f9c3 --- /dev/null +++ b/rdmo/projects/assets/js/interview/actions/configActions.js @@ -0,0 +1,23 @@ +import CoreApi from 'rdmo/core/assets/js/api/CoreApi' + +import { FETCH_CONFIG_SUCCESS, FETCH_CONFIG_ERROR, UPDATE_CONFIG } from './types' + +export function fetchConfig() { + return (dispatch) => Promise.all([ + CoreApi.fetchSettings(), + ]).then(([settings]) => dispatch(fetchConfigSuccess({ + settings + }))) +} + +export function fetchConfigSuccess(config) { + return {type: FETCH_CONFIG_SUCCESS, config} +} + +export function fetchConfigError(errors) { + return {type: FETCH_CONFIG_ERROR, errors} +} + +export function updateConfig(path, value) { + return {type: UPDATE_CONFIG, path, value} +} diff --git a/rdmo/projects/assets/js/interview/actions/types.js b/rdmo/projects/assets/js/interview/actions/types.js new file mode 100644 index 0000000000..8a94536096 --- /dev/null +++ b/rdmo/projects/assets/js/interview/actions/types.js @@ -0,0 +1,3 @@ +export const FETCH_CONFIG_SUCCESS = 'config/fetchConfigSuccess' +export const FETCH_CONFIG_ERROR = 'config/fetchConfigError' +export const UPDATE_CONFIG = 'config/updateConfig' diff --git a/rdmo/projects/assets/js/interview/api/ProjectsApi.js b/rdmo/projects/assets/js/interview/api/ProjectsApi.js new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rdmo/projects/assets/js/interview/containers/Main.js b/rdmo/projects/assets/js/interview/containers/Main.js new file mode 100644 index 0000000000..f7b4aedf22 --- /dev/null +++ b/rdmo/projects/assets/js/interview/containers/Main.js @@ -0,0 +1,38 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import * as configActions from '../actions/configActions' + +const Main = ({ config, configActions }) => { + + console.log(config) + console.log(configActions) + + // fetching the data is not complete yet, or no action was invoked yet + return ( +
+ Main +
+ ) +} + +Main.propTypes = { + config: PropTypes.object.isRequired, + configActions: PropTypes.object.isRequired +} + +function mapStateToProps(state) { + return { + config: state.config + } +} + +function mapDispatchToProps(dispatch) { + return { + configActions: bindActionCreators(configActions, dispatch) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(Main) diff --git a/rdmo/projects/assets/js/interview/containers/Sidebar.js b/rdmo/projects/assets/js/interview/containers/Sidebar.js new file mode 100644 index 0000000000..156049720f --- /dev/null +++ b/rdmo/projects/assets/js/interview/containers/Sidebar.js @@ -0,0 +1,38 @@ +import React from 'react' +import PropTypes from 'prop-types' +import { bindActionCreators } from 'redux' +import { connect } from 'react-redux' + +import * as configActions from '../actions/configActions' + +const Sidebar = ({ config, configActions }) => { + + console.log(config) + console.log(configActions) + + // fetching the data is not complete yet, or no action was invoked yet + return ( +
+ Sidebar +
+ ) +} + +Sidebar.propTypes = { + config: PropTypes.object.isRequired, + configActions: PropTypes.object.isRequired +} + +function mapStateToProps(state) { + return { + config: state.config + } +} + +function mapDispatchToProps(dispatch) { + return { + configActions: bindActionCreators(configActions, dispatch) + } +} + +export default connect(mapStateToProps, mapDispatchToProps)(Sidebar) diff --git a/rdmo/projects/assets/js/interview/reducers/configReducer.js b/rdmo/projects/assets/js/interview/reducers/configReducer.js new file mode 100644 index 0000000000..c4b81b02e0 --- /dev/null +++ b/rdmo/projects/assets/js/interview/reducers/configReducer.js @@ -0,0 +1,29 @@ +import set from 'lodash/set' + +import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' + +import { FETCH_CONFIG_SUCCESS, FETCH_CONFIG_ERROR, UPDATE_CONFIG } from '../actions/types' + +const initialState = { + baseUrl: baseUrl + '/interview/', + settings: {} +} + +export default function configReducer(state = initialState, action) { + let newState + switch(action.type) { + case UPDATE_CONFIG: + newState = {...state} + + set(newState, action.path, action.value) + localStorage.setItem(`rdmo.management.config.${action.path}`, action.value) + + return newState + case FETCH_CONFIG_SUCCESS: + return {...state, ...action.config} + case FETCH_CONFIG_ERROR: + return {...state, pending: false } + default: + return state + } +} diff --git a/rdmo/projects/assets/js/interview/reducers/rootReducer.js b/rdmo/projects/assets/js/interview/reducers/rootReducer.js new file mode 100644 index 0000000000..26ef719cbe --- /dev/null +++ b/rdmo/projects/assets/js/interview/reducers/rootReducer.js @@ -0,0 +1,9 @@ +import { combineReducers } from 'redux' + +import configReducer from './configReducer' + +const rootReducer = combineReducers({ + config: configReducer +}) + +export default rootReducer diff --git a/rdmo/projects/assets/js/interview/store/configureStore.js b/rdmo/projects/assets/js/interview/store/configureStore.js new file mode 100644 index 0000000000..6b33e43f0b --- /dev/null +++ b/rdmo/projects/assets/js/interview/store/configureStore.js @@ -0,0 +1,43 @@ +import { applyMiddleware, createStore } from 'redux' +import Cookies from 'js-cookie' +import thunk from 'redux-thunk' +import isEmpty from 'lodash/isEmpty' + +import rootReducer from '../reducers/rootReducer' + +import * as configActions from '../actions/configActions' + +export default function configureStore() { + const middlewares = [thunk] + + // empty localStorage in new session + const currentStoreId = Cookies.get('storeid') + const localStoreId = localStorage.getItem('rdmo.storeid') + + if (isEmpty(localStoreId) || localStoreId !== currentStoreId) { + localStorage.clear() + localStorage.setItem('rdmo.storeid', currentStoreId) + } + + if (process.env.NODE_ENV === 'development') { + const { logger } = require('redux-logger') + middlewares.push(logger) + } + + const store = createStore( + rootReducer, + applyMiddleware(...middlewares) + ) + + // this event is triggered when the page first loads + window.addEventListener('load', () => { + store.dispatch(configActions.fetchConfig()) + }) + + // this event is triggered when when the forward/back buttons are used + window.addEventListener('popstate', () => { + + }) + + return store +} diff --git a/rdmo/projects/templates/projects/project_interview.html b/rdmo/projects/templates/projects/project_interview.html index 761136204d..8c894b557d 100644 --- a/rdmo/projects/templates/projects/project_interview.html +++ b/rdmo/projects/templates/projects/project_interview.html @@ -25,8 +25,6 @@ {% block page %} -
- Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. Lorem ipsum dolor sit amet, consetetur sadipscing elitr, sed diam nonumy eirmod tempor invidunt ut labore et dolore magna aliquyam erat, sed diam voluptua. At vero eos et accusam et justo duo dolores et ea rebum. Stet clita kasd gubergren, no sea takimata sanctus est. Lorem ipsum dolor sit amet. -
+
{% endblock %} From 97b3a44aef41e55cddcc05756e2ee55d3f2b1375 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Fri, 15 Mar 2024 15:10:13 +0100 Subject: [PATCH 05/72] Add breadcrumb, overview, progress, buttons and navigation to interview --- .../js/interview/actions/pageActions.js | 51 +++++++++++++ .../assets/js/interview/actions/types.js | 10 +++ .../assets/js/interview/api/PagesApi.js | 15 ++++ .../assets/js/interview/api/ProjectsApi.js | 23 ++++++ .../assets/js/interview/api/ValuesApi.js | 39 ++++++++++ .../js/interview/components/Breadcrump.js | 30 ++++++++ .../assets/js/interview/components/Buttons.js | 34 +++++++++ .../js/interview/components/Navigation.js | 71 +++++++++++++++++++ .../js/interview/components/Overview.js | 43 +++++++++++ .../js/interview/components/Progress.js | 30 ++++++++ .../assets/js/interview/containers/Main.js | 37 ++++++---- .../assets/js/interview/containers/Sidebar.js | 41 +++++++---- .../js/interview/reducers/pageReducer.js | 16 +++++ .../js/interview/reducers/rootReducer.js | 4 +- .../js/interview/store/configureStore.js | 7 +- rdmo/projects/assets/scss/interview.scss | 50 ++++++++++++- 16 files changed, 472 insertions(+), 29 deletions(-) create mode 100644 rdmo/projects/assets/js/interview/actions/pageActions.js create mode 100644 rdmo/projects/assets/js/interview/api/PagesApi.js create mode 100644 rdmo/projects/assets/js/interview/api/ValuesApi.js create mode 100644 rdmo/projects/assets/js/interview/components/Breadcrump.js create mode 100644 rdmo/projects/assets/js/interview/components/Buttons.js create mode 100644 rdmo/projects/assets/js/interview/components/Navigation.js create mode 100644 rdmo/projects/assets/js/interview/components/Overview.js create mode 100644 rdmo/projects/assets/js/interview/components/Progress.js create mode 100644 rdmo/projects/assets/js/interview/reducers/pageReducer.js diff --git a/rdmo/projects/assets/js/interview/actions/pageActions.js b/rdmo/projects/assets/js/interview/actions/pageActions.js new file mode 100644 index 0000000000..84fbea6751 --- /dev/null +++ b/rdmo/projects/assets/js/interview/actions/pageActions.js @@ -0,0 +1,51 @@ +import PagesApi from '../api/PagesApi' +import ProjectsApi from '../api/ProjectsApi' +import ValuesApi from '../api/ValuesApi' + +import { FETCH_PAGE_SUCCESS, FETCH_PAGE_ERROR, UPLOAD_FILE_SUCCESS, UPLOAD_FILE_ERROR, JUMP, PREV, NEXT } from './types' + +export function fetchPage() { + return (dispatch) => Promise.all([ + PagesApi.fetchContinue(12), + ProjectsApi.fetchProject(12), + ProjectsApi.fetchOverview(12), + ProjectsApi.fetchProgress(12), + ProjectsApi.fetchNavigation(12, 1) + ]).then(([page, project, overview, progress, navigation]) => dispatch(fetchPageSuccess({ + page, project, overview, progress, navigation + }))) +} + +export function fetchPageSuccess(page) { + return {type: FETCH_PAGE_SUCCESS, page} +} + +export function fetchPageError(errors) { + return {type: FETCH_PAGE_ERROR, errors} +} + +export function uploadFile(projectId, valueId, file) { + return (dispatch) => ValuesApi.uploadFile(projectId, valueId, file).then((value) => { + dispatch(uploadFileSuccess(value)) + }) +} + +export function uploadFileSuccess(value) { + return {type: UPLOAD_FILE_SUCCESS, value} +} + +export function uploadFileError(value) { + return {type: UPLOAD_FILE_ERROR, value} +} + +export function jump(section, page = null) { + return {type: JUMP, section, page} +} + +export function prev() { + return {type: PREV} +} + +export function next() { + return {type: NEXT} +} diff --git a/rdmo/projects/assets/js/interview/actions/types.js b/rdmo/projects/assets/js/interview/actions/types.js index 8a94536096..f04a109c02 100644 --- a/rdmo/projects/assets/js/interview/actions/types.js +++ b/rdmo/projects/assets/js/interview/actions/types.js @@ -1,3 +1,13 @@ export const FETCH_CONFIG_SUCCESS = 'config/fetchConfigSuccess' export const FETCH_CONFIG_ERROR = 'config/fetchConfigError' export const UPDATE_CONFIG = 'config/updateConfig' + +export const FETCH_PAGE_SUCCESS = 'page/fetchPageSuccess' +export const FETCH_PAGE_ERROR = 'page/fetchPageError' + +export const UPLOAD_FILE_SUCCESS = 'page/uploadFileSuccess' +export const UPLOAD_FILE_ERROR = 'page/uploadFileError' + +export const JUMP = 'page/jump' +export const NEXT = 'page/next' +export const PREV = 'page/prev' diff --git a/rdmo/projects/assets/js/interview/api/PagesApi.js b/rdmo/projects/assets/js/interview/api/PagesApi.js new file mode 100644 index 0000000000..f71993fbca --- /dev/null +++ b/rdmo/projects/assets/js/interview/api/PagesApi.js @@ -0,0 +1,15 @@ +import BaseApi from 'rdmo/core/assets/js/api/BaseApi' + +class ProjectsApi extends BaseApi { + + static fetchPage(projectId, page_id) { + return this.get(`/api/v1/projects/projects/${projectId}/pages/${page_id}`) + } + + static fetchContinue(projectId) { + return this.get(`/api/v1/projects/projects/${projectId}/pages/continue/`) + } + +} + +export default ProjectsApi diff --git a/rdmo/projects/assets/js/interview/api/ProjectsApi.js b/rdmo/projects/assets/js/interview/api/ProjectsApi.js index e69de29bb2..38117ff0d5 100644 --- a/rdmo/projects/assets/js/interview/api/ProjectsApi.js +++ b/rdmo/projects/assets/js/interview/api/ProjectsApi.js @@ -0,0 +1,23 @@ +import BaseApi from 'rdmo/core/assets/js/api/BaseApi' + +class ProjectsApi extends BaseApi { + + static fetchProject(projectId) { + return this.get(`/api/v1/projects/projects/${projectId}/`) + } + + static fetchOverview(projectId) { + return this.get(`/api/v1/projects/projects/${projectId}/overview/`) + } + + static fetchNavigation(projectId, page_id) { + return this.get(`/api/v1/projects/projects/${projectId}/navigation/${page_id}`) + } + + static fetchProgress(projectId) { + return this.get(`/api/v1/projects/projects/${projectId}/progress/`) + } + +} + +export default ProjectsApi diff --git a/rdmo/projects/assets/js/interview/api/ValuesApi.js b/rdmo/projects/assets/js/interview/api/ValuesApi.js new file mode 100644 index 0000000000..2add6f4716 --- /dev/null +++ b/rdmo/projects/assets/js/interview/api/ValuesApi.js @@ -0,0 +1,39 @@ +import Cookies from 'js-cookie' + +import BaseApi from 'rdmo/core/assets/js/api/BaseApi' + +import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' + +class ValuesApi extends BaseApi { + + static uploadFile(projectId, valueId, file) { + const url = `/api/v1/projects/projects/${projectId}/values/${valueId}/file/` + + var formData = new FormData() + formData.append('method', 'upload_file') + formData.append('file', file) + + return fetch(baseUrl + url, { + method: 'POST', + headers: { + 'X-CSRFToken': Cookies.get('csrftoken') + }, + body: formData + }).catch(error => { + throw new Error(`API error: ${error.message}`) + }).then(response => { + if (response.ok) { + return response.json() + } else if (response.status == 400) { + // return response.json().then(errors => { + // throw new ValidationError(errors) + // }) + } else { + // throw new ApiError(response.statusText, response.status) + } + }) + } + +} + +export default ValuesApi diff --git a/rdmo/projects/assets/js/interview/components/Breadcrump.js b/rdmo/projects/assets/js/interview/components/Breadcrump.js new file mode 100644 index 0000000000..624e49c53b --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/Breadcrump.js @@ -0,0 +1,30 @@ +import React from 'react' +import PropTypes from 'prop-types' + +const Breadcrump = ({ project, page, onJump }) => { + return ( + + ) +} + +Breadcrump.propTypes = { + project: PropTypes.object.isRequired, + page: PropTypes.object.isRequired, + onJump: PropTypes.func.isRequired +} + +export default Breadcrump diff --git a/rdmo/projects/assets/js/interview/components/Buttons.js b/rdmo/projects/assets/js/interview/components/Buttons.js new file mode 100644 index 0000000000..603d2adc08 --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/Buttons.js @@ -0,0 +1,34 @@ +import React from 'react' +import PropTypes from 'prop-types' + +const Buttons = ({ currentPage, onPrev, onNext }) => { + return ( + <> +
+
+ +
+ +
+ +
+
+ + ) +} + +Buttons.propTypes = { + currentPage: PropTypes.object.isRequired, + onPrev: PropTypes.func.isRequired, + onNext: PropTypes.func.isRequired +} + +export default Buttons diff --git a/rdmo/projects/assets/js/interview/components/Navigation.js b/rdmo/projects/assets/js/interview/components/Navigation.js new file mode 100644 index 0000000000..096e547810 --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/Navigation.js @@ -0,0 +1,71 @@ +import React from 'react' +import PropTypes from 'prop-types' +import classNames from 'classnames' + +const Navigation = ({ currentPage, navigation, onJump }) => { + + const handleJump = (event, section, page) => { + event.preventDefault() + onJump(section, page) + } + + return ( + <> +

{gettext('Navigation')}

+ + + + ) +} + +Navigation.propTypes = { + currentPage: PropTypes.object.isRequired, + navigation: PropTypes.array.isRequired, + onJump: PropTypes.func.isRequired +} + +export default Navigation diff --git a/rdmo/projects/assets/js/interview/components/Overview.js b/rdmo/projects/assets/js/interview/components/Overview.js new file mode 100644 index 0000000000..7f37f4aaf3 --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/Overview.js @@ -0,0 +1,43 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' + +const Overview = ({ project }) => { + + const projectsUrl = `${baseUrl}/projects/` + const projectUrl = `${baseUrl}/projects/${project.id}` + + return ( + <> +

{gettext('Overview')}

+ +
+
    +
  • + {gettext('Project')}: {project.title} +
  • +
  • + {/* TODO: get catalog title from catalog api */} + {gettext('Catalog')}: {project.catalog} +
  • +
+ + +
+ + ) +} + +Overview.propTypes = { + project: PropTypes.object.isRequired +} + +export default Overview diff --git a/rdmo/projects/assets/js/interview/components/Progress.js b/rdmo/projects/assets/js/interview/components/Progress.js new file mode 100644 index 0000000000..59de5e33d2 --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/Progress.js @@ -0,0 +1,30 @@ +import React from 'react' +import PropTypes from 'prop-types' + +const Progress = ({ progress }) => { + const low = progress.ratio <= 0.25 + const width = progress.ratio * 100 + const label = interpolate(gettext('%s of %s'), [progress.count, progress.total]) + + return ( + <> +

{gettext('Progress')}

+ +
+ {low &&
} + +
+
+ {!low && } +
+
+
+ + ) +} + +Progress.propTypes = { + progress: PropTypes.object.isRequired +} + +export default Progress diff --git a/rdmo/projects/assets/js/interview/containers/Main.js b/rdmo/projects/assets/js/interview/containers/Main.js index f7b4aedf22..9fd81b6531 100644 --- a/rdmo/projects/assets/js/interview/containers/Main.js +++ b/rdmo/projects/assets/js/interview/containers/Main.js @@ -3,35 +3,46 @@ import PropTypes from 'prop-types' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' -import * as configActions from '../actions/configActions' - -const Main = ({ config, configActions }) => { +import Breadcrump from '../components/Breadcrump' - console.log(config) - console.log(configActions) +import * as configActions from '../actions/configActions' +import * as pageActions from '../actions/pageActions' + +// eslint-disable-next-line no-unused-vars +const Main = ({ config, page, configActions, pageActions }) => { + if (page.display) { + return ( +
+ pageActions.jump(pageId)} /> +

+ {page.page.title} +

+
+ ) + } // fetching the data is not complete yet, or no action was invoked yet - return ( -
- Main -
- ) + return null } Main.propTypes = { config: PropTypes.object.isRequired, - configActions: PropTypes.object.isRequired + page: PropTypes.object.isRequired, + configActions: PropTypes.object.isRequired, + pageActions: PropTypes.object.isRequired } function mapStateToProps(state) { return { - config: state.config + config: state.config, + page: state.page } } function mapDispatchToProps(dispatch) { return { - configActions: bindActionCreators(configActions, dispatch) + configActions: bindActionCreators(configActions, dispatch), + pageActions: bindActionCreators(pageActions, dispatch) } } diff --git a/rdmo/projects/assets/js/interview/containers/Sidebar.js b/rdmo/projects/assets/js/interview/containers/Sidebar.js index 156049720f..4eb0c6535c 100644 --- a/rdmo/projects/assets/js/interview/containers/Sidebar.js +++ b/rdmo/projects/assets/js/interview/containers/Sidebar.js @@ -3,35 +3,50 @@ import PropTypes from 'prop-types' import { bindActionCreators } from 'redux' import { connect } from 'react-redux' -import * as configActions from '../actions/configActions' - -const Sidebar = ({ config, configActions }) => { +import Buttons from '../components/Buttons' +import Navigation from '../components/Navigation' +import Overview from '../components/Overview' +import Progress from '../components/Progress' - console.log(config) - console.log(configActions) +import * as configActions from '../actions/configActions' +import * as pageActions from '../actions/pageActions' + +// eslint-disable-next-line no-unused-vars +const Sidebar = ({ config, page, configActions, pageActions }) => { + if (page.display) { + return ( +
+ + + pageActions.prev()} onNext={() => pageActions.next()} /> + pageActions.jump(sectionId, pageId)} /> +
+ ) + } // fetching the data is not complete yet, or no action was invoked yet - return ( -
- Sidebar -
- ) + return null } Sidebar.propTypes = { config: PropTypes.object.isRequired, - configActions: PropTypes.object.isRequired + page: PropTypes.object.isRequired, + configActions: PropTypes.object.isRequired, + pageActions: PropTypes.object.isRequired } function mapStateToProps(state) { return { - config: state.config + config: state.config, + page: state.page } } function mapDispatchToProps(dispatch) { return { - configActions: bindActionCreators(configActions, dispatch) + configActions: bindActionCreators(configActions, dispatch), + pageActions: bindActionCreators(pageActions, dispatch) } } diff --git a/rdmo/projects/assets/js/interview/reducers/pageReducer.js b/rdmo/projects/assets/js/interview/reducers/pageReducer.js new file mode 100644 index 0000000000..72bb0f110f --- /dev/null +++ b/rdmo/projects/assets/js/interview/reducers/pageReducer.js @@ -0,0 +1,16 @@ +import { FETCH_PAGE_SUCCESS, FETCH_PAGE_ERROR } from '../actions/types' + +const initialState = { + display: false +} + +export default function configReducer(state = initialState, action) { + switch(action.type) { + case FETCH_PAGE_SUCCESS: + return {...state, ...action.page, display: true} + case FETCH_PAGE_ERROR: + return {...state } + default: + return state + } +} diff --git a/rdmo/projects/assets/js/interview/reducers/rootReducer.js b/rdmo/projects/assets/js/interview/reducers/rootReducer.js index 26ef719cbe..b4866f767f 100644 --- a/rdmo/projects/assets/js/interview/reducers/rootReducer.js +++ b/rdmo/projects/assets/js/interview/reducers/rootReducer.js @@ -1,9 +1,11 @@ import { combineReducers } from 'redux' import configReducer from './configReducer' +import pageReducer from './pageReducer' const rootReducer = combineReducers({ - config: configReducer + config: configReducer, + page: pageReducer }) export default rootReducer diff --git a/rdmo/projects/assets/js/interview/store/configureStore.js b/rdmo/projects/assets/js/interview/store/configureStore.js index 6b33e43f0b..f530eae6d3 100644 --- a/rdmo/projects/assets/js/interview/store/configureStore.js +++ b/rdmo/projects/assets/js/interview/store/configureStore.js @@ -6,6 +6,7 @@ import isEmpty from 'lodash/isEmpty' import rootReducer from '../reducers/rootReducer' import * as configActions from '../actions/configActions' +import * as pageActions from '../actions/pageActions' export default function configureStore() { const middlewares = [thunk] @@ -29,9 +30,13 @@ export default function configureStore() { applyMiddleware(...middlewares) ) + const fetchConfig = () => store.dispatch(configActions.fetchConfig()) + + const fetchPage = () => store.dispatch(pageActions.fetchPage()) + // this event is triggered when the page first loads window.addEventListener('load', () => { - store.dispatch(configActions.fetchConfig()) + fetchConfig().then(() => fetchPage()) }) // this event is triggered when when the forward/back buttons are used diff --git a/rdmo/projects/assets/scss/interview.scss b/rdmo/projects/assets/scss/interview.scss index 0ae12b5d5c..c1658500bf 100644 --- a/rdmo/projects/assets/scss/interview.scss +++ b/rdmo/projects/assets/scss/interview.scss @@ -1,3 +1,51 @@ -.interview { +.interview-breadcrumb { + margin-top: 20px; + margin-bottom: 12px; + background-color: transparent; + border: 1px solid #ccc; +} + +.interview-navigation { + line-height: 24px; + + h4 { + line-height: 20px; + margin-top: 20px; + margin-bottom: 10px; + } + a { + display: block; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; + } + ul { + margin-left: 0; + } + li ul li { + margin-left: 20px; + } + li.active { + margin-left: 0; + } + li.active a:before { + float: left; + width: 20px; + text-align: right; + content: '\2192\0000a0'; /* right-arrow followed by a space */ + } +} + +.interview-progress { + .progress { + margin-bottom: 10px; + } + + .interview-progress-count { + position: absolute; + left: 0; + right: 0; + text-align: center; + } } From 6cbd09a2e6eb5c98773587ce317086455f4dc946 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Mon, 25 Mar 2024 19:34:44 +0100 Subject: [PATCH 06/72] Add location handling and refactor --- .../js/interview/actions/interviewActions.js | 80 +++++++++++++++++++ .../js/interview/actions/pageActions.js | 51 ------------ .../assets/js/interview/actions/types.js | 17 ++-- .../interview/api/{PagesApi.js => PageApi.js} | 4 +- .../api/{ProjectsApi.js => ProjectApi.js} | 4 - .../assets/js/interview/api/ValuesApi.js | 39 --------- .../js/interview/components/Breadcrump.js | 24 ++++-- .../assets/js/interview/components/Buttons.js | 11 ++- .../js/interview/components/Navigation.js | 38 ++++----- .../js/interview/components/Overview.js | 10 +-- .../assets/js/interview/containers/Main.js | 18 ++--- .../assets/js/interview/containers/Sidebar.js | 23 +++--- .../js/interview/reducers/configReducer.js | 3 - .../js/interview/reducers/interviewReducer.js | 34 ++++++++ .../js/interview/reducers/pageReducer.js | 16 ---- .../js/interview/reducers/rootReducer.js | 11 --- .../js/interview/store/configureStore.js | 29 +++++-- .../assets/js/interview/utils/location.js | 26 ++++++ .../assets/js/interview/utils/projectId.js | 2 + .../templates/projects/project_interview.html | 4 + 20 files changed, 243 insertions(+), 201 deletions(-) create mode 100644 rdmo/projects/assets/js/interview/actions/interviewActions.js delete mode 100644 rdmo/projects/assets/js/interview/actions/pageActions.js rename rdmo/projects/assets/js/interview/api/{PagesApi.js => PageApi.js} (84%) rename rdmo/projects/assets/js/interview/api/{ProjectsApi.js => ProjectApi.js} (82%) delete mode 100644 rdmo/projects/assets/js/interview/api/ValuesApi.js create mode 100644 rdmo/projects/assets/js/interview/reducers/interviewReducer.js delete mode 100644 rdmo/projects/assets/js/interview/reducers/pageReducer.js delete mode 100644 rdmo/projects/assets/js/interview/reducers/rootReducer.js create mode 100644 rdmo/projects/assets/js/interview/utils/location.js create mode 100644 rdmo/projects/assets/js/interview/utils/projectId.js diff --git a/rdmo/projects/assets/js/interview/actions/interviewActions.js b/rdmo/projects/assets/js/interview/actions/interviewActions.js new file mode 100644 index 0000000000..d00acc3d00 --- /dev/null +++ b/rdmo/projects/assets/js/interview/actions/interviewActions.js @@ -0,0 +1,80 @@ +import isNil from 'lodash/isNil' + +import PageApi from '../api/PageApi' +import ProjectApi from '../api/ProjectApi' + +import { updateLocation } from '../utils/location' +import projectId from '../utils/projectId' + +import { + FETCH_NAVIGATION_ERROR, + FETCH_NAVIGATION_SUCCESS, + FETCH_OVERVIEW_ERROR, + FETCH_OVERVIEW_SUCCESS, + FETCH_PAGE_ERROR, + FETCH_PAGE_SUCCESS, + FETCH_PROGRESS_ERROR, + FETCH_PROGRESS_SUCCESS, +} from './types' + +export function fetchOverview() { + return (dispatch) => ProjectApi.fetchOverview(projectId) + .then((overview) => dispatch(fetchOverviewSuccess(overview))) + .catch((errors) => dispatch(fetchOverviewError(errors))) +} + +export function fetchOverviewSuccess(overview) { + return {type: FETCH_OVERVIEW_SUCCESS, overview} +} + +export function fetchOverviewError(errors) { + return {type: FETCH_OVERVIEW_ERROR, errors} +} + +export function fetchProgress() { + return (dispatch) => ProjectApi.fetchProgress(projectId) + .then((progress) => dispatch(fetchProgressSuccess(progress))) + .catch((errors) => dispatch(fetchProgressError(errors))) +} + +export function fetchProgressSuccess(progress) { + return {type: FETCH_PROGRESS_SUCCESS, progress} +} + +export function fetchProgressError(errors) { + return {type: FETCH_PROGRESS_ERROR, errors} +} + +export function fetchNavigation(sectionId) { + return (dispatch) => ProjectApi.fetchNavigation(projectId, sectionId) + .then((navigation) => dispatch(fetchNavigationSuccess(navigation))) + .catch((errors) => dispatch(fetchNavigationError(errors))) +} + +export function fetchNavigationSuccess(navigation) { + return {type: FETCH_NAVIGATION_SUCCESS, navigation} +} + +export function fetchNavigationError(errors) { + return {type: FETCH_NAVIGATION_ERROR, errors} +} + +export function fetchPage(pageId) { + return (dispatch) => { + const promise = isNil(pageId) ? PageApi.fetchContinue(projectId) + : PageApi.fetchPage(projectId, pageId) + return promise.then((page) => { + updateLocation(page.id) + dispatch(fetchNavigation(page.section.id)) + dispatch(fetchPageSuccess(page)) + }) + } +} + +export function fetchPageSuccess(page) { + return {type: FETCH_PAGE_SUCCESS, page} +} + +export function fetchPageError(errors) { + return {type: FETCH_PAGE_ERROR, errors} +} diff --git a/rdmo/projects/assets/js/interview/actions/pageActions.js b/rdmo/projects/assets/js/interview/actions/pageActions.js deleted file mode 100644 index 84fbea6751..0000000000 --- a/rdmo/projects/assets/js/interview/actions/pageActions.js +++ /dev/null @@ -1,51 +0,0 @@ -import PagesApi from '../api/PagesApi' -import ProjectsApi from '../api/ProjectsApi' -import ValuesApi from '../api/ValuesApi' - -import { FETCH_PAGE_SUCCESS, FETCH_PAGE_ERROR, UPLOAD_FILE_SUCCESS, UPLOAD_FILE_ERROR, JUMP, PREV, NEXT } from './types' - -export function fetchPage() { - return (dispatch) => Promise.all([ - PagesApi.fetchContinue(12), - ProjectsApi.fetchProject(12), - ProjectsApi.fetchOverview(12), - ProjectsApi.fetchProgress(12), - ProjectsApi.fetchNavigation(12, 1) - ]).then(([page, project, overview, progress, navigation]) => dispatch(fetchPageSuccess({ - page, project, overview, progress, navigation - }))) -} - -export function fetchPageSuccess(page) { - return {type: FETCH_PAGE_SUCCESS, page} -} - -export function fetchPageError(errors) { - return {type: FETCH_PAGE_ERROR, errors} -} - -export function uploadFile(projectId, valueId, file) { - return (dispatch) => ValuesApi.uploadFile(projectId, valueId, file).then((value) => { - dispatch(uploadFileSuccess(value)) - }) -} - -export function uploadFileSuccess(value) { - return {type: UPLOAD_FILE_SUCCESS, value} -} - -export function uploadFileError(value) { - return {type: UPLOAD_FILE_ERROR, value} -} - -export function jump(section, page = null) { - return {type: JUMP, section, page} -} - -export function prev() { - return {type: PREV} -} - -export function next() { - return {type: NEXT} -} diff --git a/rdmo/projects/assets/js/interview/actions/types.js b/rdmo/projects/assets/js/interview/actions/types.js index f04a109c02..e07c2acb8b 100644 --- a/rdmo/projects/assets/js/interview/actions/types.js +++ b/rdmo/projects/assets/js/interview/actions/types.js @@ -2,12 +2,11 @@ export const FETCH_CONFIG_SUCCESS = 'config/fetchConfigSuccess' export const FETCH_CONFIG_ERROR = 'config/fetchConfigError' export const UPDATE_CONFIG = 'config/updateConfig' -export const FETCH_PAGE_SUCCESS = 'page/fetchPageSuccess' -export const FETCH_PAGE_ERROR = 'page/fetchPageError' - -export const UPLOAD_FILE_SUCCESS = 'page/uploadFileSuccess' -export const UPLOAD_FILE_ERROR = 'page/uploadFileError' - -export const JUMP = 'page/jump' -export const NEXT = 'page/next' -export const PREV = 'page/prev' +export const FETCH_NAVIGATION_ERROR = 'interview/fetchNavigationSuccess' +export const FETCH_NAVIGATION_SUCCESS = 'interview/fetchNavigationSuccess' +export const FETCH_OVERVIEW_ERROR = 'interview/fetchOverviewSuccess' +export const FETCH_OVERVIEW_SUCCESS = 'interview/fetchOverviewSuccess' +export const FETCH_PAGE_ERROR = 'interview/fetchPageError' +export const FETCH_PAGE_SUCCESS = 'interview/fetchPageSuccess' +export const FETCH_PROGRESS_ERROR = 'interview/fetchProgressSuccess' +export const FETCH_PROGRESS_SUCCESS = 'interview/fetchProgressSuccess' diff --git a/rdmo/projects/assets/js/interview/api/PagesApi.js b/rdmo/projects/assets/js/interview/api/PageApi.js similarity index 84% rename from rdmo/projects/assets/js/interview/api/PagesApi.js rename to rdmo/projects/assets/js/interview/api/PageApi.js index f71993fbca..49f3d45027 100644 --- a/rdmo/projects/assets/js/interview/api/PagesApi.js +++ b/rdmo/projects/assets/js/interview/api/PageApi.js @@ -2,8 +2,8 @@ import BaseApi from 'rdmo/core/assets/js/api/BaseApi' class ProjectsApi extends BaseApi { - static fetchPage(projectId, page_id) { - return this.get(`/api/v1/projects/projects/${projectId}/pages/${page_id}`) + static fetchPage(projectId, pageId) { + return this.get(`/api/v1/projects/projects/${projectId}/pages/${pageId}`) } static fetchContinue(projectId) { diff --git a/rdmo/projects/assets/js/interview/api/ProjectsApi.js b/rdmo/projects/assets/js/interview/api/ProjectApi.js similarity index 82% rename from rdmo/projects/assets/js/interview/api/ProjectsApi.js rename to rdmo/projects/assets/js/interview/api/ProjectApi.js index 38117ff0d5..216efc85be 100644 --- a/rdmo/projects/assets/js/interview/api/ProjectsApi.js +++ b/rdmo/projects/assets/js/interview/api/ProjectApi.js @@ -2,10 +2,6 @@ import BaseApi from 'rdmo/core/assets/js/api/BaseApi' class ProjectsApi extends BaseApi { - static fetchProject(projectId) { - return this.get(`/api/v1/projects/projects/${projectId}/`) - } - static fetchOverview(projectId) { return this.get(`/api/v1/projects/projects/${projectId}/overview/`) } diff --git a/rdmo/projects/assets/js/interview/api/ValuesApi.js b/rdmo/projects/assets/js/interview/api/ValuesApi.js deleted file mode 100644 index 2add6f4716..0000000000 --- a/rdmo/projects/assets/js/interview/api/ValuesApi.js +++ /dev/null @@ -1,39 +0,0 @@ -import Cookies from 'js-cookie' - -import BaseApi from 'rdmo/core/assets/js/api/BaseApi' - -import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' - -class ValuesApi extends BaseApi { - - static uploadFile(projectId, valueId, file) { - const url = `/api/v1/projects/projects/${projectId}/values/${valueId}/file/` - - var formData = new FormData() - formData.append('method', 'upload_file') - formData.append('file', file) - - return fetch(baseUrl + url, { - method: 'POST', - headers: { - 'X-CSRFToken': Cookies.get('csrftoken') - }, - body: formData - }).catch(error => { - throw new Error(`API error: ${error.message}`) - }).then(response => { - if (response.ok) { - return response.json() - } else if (response.status == 400) { - // return response.json().then(errors => { - // throw new ValidationError(errors) - // }) - } else { - // throw new ApiError(response.statusText, response.status) - } - }) - } - -} - -export default ValuesApi diff --git a/rdmo/projects/assets/js/interview/components/Breadcrump.js b/rdmo/projects/assets/js/interview/components/Breadcrump.js index 624e49c53b..a7b5f4842d 100644 --- a/rdmo/projects/assets/js/interview/components/Breadcrump.js +++ b/rdmo/projects/assets/js/interview/components/Breadcrump.js @@ -1,19 +1,29 @@ import React from 'react' import PropTypes from 'prop-types' -const Breadcrump = ({ project, page, onJump }) => { +import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' + +const Breadcrump = ({ overview, page, onClick }) => { + + const handleClick = (event) => { + event.preventDefault() + onClick(page.section.first) + } + return (
  • - {gettext('My Projects')} + + {gettext('My Projects')} +
  • - - {project.title} + + {overview.title}
  • - onJump(page.section.id)}> + {page.section.title}
  • @@ -22,9 +32,9 @@ const Breadcrump = ({ project, page, onJump }) => { } Breadcrump.propTypes = { - project: PropTypes.object.isRequired, + overview: PropTypes.object.isRequired, page: PropTypes.object.isRequired, - onJump: PropTypes.func.isRequired + onClick: PropTypes.func.isRequired } export default Breadcrump diff --git a/rdmo/projects/assets/js/interview/components/Buttons.js b/rdmo/projects/assets/js/interview/components/Buttons.js index 603d2adc08..b2581adfb7 100644 --- a/rdmo/projects/assets/js/interview/components/Buttons.js +++ b/rdmo/projects/assets/js/interview/components/Buttons.js @@ -1,12 +1,12 @@ import React from 'react' import PropTypes from 'prop-types' -const Buttons = ({ currentPage, onPrev, onNext }) => { +const Buttons = ({ page, onClick }) => { return ( <>
    -
    - @@ -26,9 +26,8 @@ const Buttons = ({ currentPage, onPrev, onNext }) => { } Buttons.propTypes = { - currentPage: PropTypes.object.isRequired, - onPrev: PropTypes.func.isRequired, - onNext: PropTypes.func.isRequired + page: PropTypes.object.isRequired, + onClick: PropTypes.func.isRequired } export default Buttons diff --git a/rdmo/projects/assets/js/interview/components/Navigation.js b/rdmo/projects/assets/js/interview/components/Navigation.js index 096e547810..6fba977089 100644 --- a/rdmo/projects/assets/js/interview/components/Navigation.js +++ b/rdmo/projects/assets/js/interview/components/Navigation.js @@ -2,11 +2,11 @@ import React from 'react' import PropTypes from 'prop-types' import classNames from 'classnames' -const Navigation = ({ currentPage, navigation, onJump }) => { +const Navigation = ({ page, navigation, onClick }) => { - const handleJump = (event, section, page) => { + const handleClick = (event, pageId) => { event.preventDefault() - onJump(section, page) + onClick(pageId) } return ( @@ -15,37 +15,37 @@ const Navigation = ({ currentPage, navigation, onJump }) => {
      { - navigation.map((section, sectionIndex) => ( -
    • - handleJump(event, section)}> - {section.title} + navigation.map((s, sIndex) => ( +
    • + handleClick(event, s.first)}> + {s.title} { - section.pages && ( + s.pages && (
        { - section.pages.map((page, pageIndex) => ( -
      • + s.pages.map((p, pIndex) => ( +
      • { - page.show ? ( - handleJump(event, section, page)}> - {page.title} + p.show ? ( + handleClick(event, p.id)}> + {p.title} { - page.count > 0 && page.count == page.total && ( + p.count > 0 && p.count == p.total && ( ) } { - page.count > 0 && page.count != page.total && ( + p.count > 0 && p.count != p.total && ( + __html: interpolate(gettext('(%s of %s)'), [p.count, p.total])}} /> ) } ) : ( - {page.title} + {p.title} ) }
      • @@ -63,9 +63,9 @@ const Navigation = ({ currentPage, navigation, onJump }) => { } Navigation.propTypes = { - currentPage: PropTypes.object.isRequired, + page: PropTypes.object.isRequired, navigation: PropTypes.array.isRequired, - onJump: PropTypes.func.isRequired + onClick: PropTypes.func.isRequired } export default Navigation diff --git a/rdmo/projects/assets/js/interview/components/Overview.js b/rdmo/projects/assets/js/interview/components/Overview.js index 7f37f4aaf3..6b4b3d3bcd 100644 --- a/rdmo/projects/assets/js/interview/components/Overview.js +++ b/rdmo/projects/assets/js/interview/components/Overview.js @@ -3,10 +3,10 @@ import PropTypes from 'prop-types' import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' -const Overview = ({ project }) => { +const Overview = ({ overview }) => { const projectsUrl = `${baseUrl}/projects/` - const projectUrl = `${baseUrl}/projects/${project.id}` + const projectUrl = `${baseUrl}/projects/${overview.id}` return ( <> @@ -15,11 +15,11 @@ const Overview = ({ project }) => {
        • - {gettext('Project')}: {project.title} + {gettext('Project')}: {overview.title}
        • {/* TODO: get catalog title from catalog api */} - {gettext('Catalog')}: {project.catalog} + {gettext('Catalog')}: {overview.catalog.title}
        @@ -37,7 +37,7 @@ const Overview = ({ project }) => { } Overview.propTypes = { - project: PropTypes.object.isRequired + overview: PropTypes.object.isRequired } export default Overview diff --git a/rdmo/projects/assets/js/interview/containers/Main.js b/rdmo/projects/assets/js/interview/containers/Main.js index 9fd81b6531..42b19c39ec 100644 --- a/rdmo/projects/assets/js/interview/containers/Main.js +++ b/rdmo/projects/assets/js/interview/containers/Main.js @@ -6,16 +6,16 @@ import { connect } from 'react-redux' import Breadcrump from '../components/Breadcrump' import * as configActions from '../actions/configActions' -import * as pageActions from '../actions/pageActions' +import * as interviewActions from '../actions/interviewActions' // eslint-disable-next-line no-unused-vars -const Main = ({ config, page, configActions, pageActions }) => { - if (page.display) { +const Main = ({ config, interview, configActions, interviewActions }) => { + if (interview.show) { return (
        - pageActions.jump(pageId)} /> +

        - {page.page.title} + {interview.page.title}

        ) @@ -27,22 +27,22 @@ const Main = ({ config, page, configActions, pageActions }) => { Main.propTypes = { config: PropTypes.object.isRequired, - page: PropTypes.object.isRequired, + interview: PropTypes.object.isRequired, configActions: PropTypes.object.isRequired, - pageActions: PropTypes.object.isRequired + interviewActions: PropTypes.object.isRequired } function mapStateToProps(state) { return { config: state.config, - page: state.page + interview: state.interview } } function mapDispatchToProps(dispatch) { return { configActions: bindActionCreators(configActions, dispatch), - pageActions: bindActionCreators(pageActions, dispatch) + interviewActions: bindActionCreators(interviewActions, dispatch), } } diff --git a/rdmo/projects/assets/js/interview/containers/Sidebar.js b/rdmo/projects/assets/js/interview/containers/Sidebar.js index 4eb0c6535c..17b5538c3e 100644 --- a/rdmo/projects/assets/js/interview/containers/Sidebar.js +++ b/rdmo/projects/assets/js/interview/containers/Sidebar.js @@ -9,18 +9,17 @@ import Overview from '../components/Overview' import Progress from '../components/Progress' import * as configActions from '../actions/configActions' -import * as pageActions from '../actions/pageActions' +import * as interviewActions from '../actions/interviewActions' // eslint-disable-next-line no-unused-vars -const Sidebar = ({ config, page, configActions, pageActions }) => { - if (page.display) { +const Sidebar = ({ config, interview, configActions, interviewActions }) => { + if (interview.show) { return (
        - - - pageActions.prev()} onNext={() => pageActions.next()} /> - pageActions.jump(sectionId, pageId)} /> + + + +
        ) } @@ -31,22 +30,22 @@ const Sidebar = ({ config, page, configActions, pageActions }) => { Sidebar.propTypes = { config: PropTypes.object.isRequired, - page: PropTypes.object.isRequired, + interview: PropTypes.object.isRequired, configActions: PropTypes.object.isRequired, - pageActions: PropTypes.object.isRequired + interviewActions: PropTypes.object.isRequired } function mapStateToProps(state) { return { config: state.config, - page: state.page + interview: state.interview } } function mapDispatchToProps(dispatch) { return { configActions: bindActionCreators(configActions, dispatch), - pageActions: bindActionCreators(pageActions, dispatch) + interviewActions: bindActionCreators(interviewActions, dispatch), } } diff --git a/rdmo/projects/assets/js/interview/reducers/configReducer.js b/rdmo/projects/assets/js/interview/reducers/configReducer.js index c4b81b02e0..b598e95ac1 100644 --- a/rdmo/projects/assets/js/interview/reducers/configReducer.js +++ b/rdmo/projects/assets/js/interview/reducers/configReducer.js @@ -1,11 +1,8 @@ import set from 'lodash/set' -import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' - import { FETCH_CONFIG_SUCCESS, FETCH_CONFIG_ERROR, UPDATE_CONFIG } from '../actions/types' const initialState = { - baseUrl: baseUrl + '/interview/', settings: {} } diff --git a/rdmo/projects/assets/js/interview/reducers/interviewReducer.js b/rdmo/projects/assets/js/interview/reducers/interviewReducer.js new file mode 100644 index 0000000000..f4b8387f76 --- /dev/null +++ b/rdmo/projects/assets/js/interview/reducers/interviewReducer.js @@ -0,0 +1,34 @@ +import { + FETCH_NAVIGATION_ERROR, + FETCH_NAVIGATION_SUCCESS, + FETCH_OVERVIEW_ERROR, + FETCH_OVERVIEW_SUCCESS, + FETCH_PAGE_ERROR, + FETCH_PAGE_SUCCESS, + FETCH_PROGRESS_ERROR, + FETCH_PROGRESS_SUCCESS, +} from '../actions/types' + +const initialState = { + show: false +} + +export default function configReducer(state = initialState, action) { + switch(action.type) { + case FETCH_OVERVIEW_SUCCESS: + return { ...state, overview: action.overview } + case FETCH_PROGRESS_SUCCESS: + return { ...state, progress: action.progress } + case FETCH_NAVIGATION_SUCCESS: + return { ...state, navigation: action.navigation, show: true } + case FETCH_PAGE_SUCCESS: + return { ...state, page: action.page } + case FETCH_OVERVIEW_ERROR: + case FETCH_PROGRESS_ERROR: + case FETCH_NAVIGATION_ERROR: + case FETCH_PAGE_ERROR: + return { errors: action.errors } + default: + return state + } +} diff --git a/rdmo/projects/assets/js/interview/reducers/pageReducer.js b/rdmo/projects/assets/js/interview/reducers/pageReducer.js deleted file mode 100644 index 72bb0f110f..0000000000 --- a/rdmo/projects/assets/js/interview/reducers/pageReducer.js +++ /dev/null @@ -1,16 +0,0 @@ -import { FETCH_PAGE_SUCCESS, FETCH_PAGE_ERROR } from '../actions/types' - -const initialState = { - display: false -} - -export default function configReducer(state = initialState, action) { - switch(action.type) { - case FETCH_PAGE_SUCCESS: - return {...state, ...action.page, display: true} - case FETCH_PAGE_ERROR: - return {...state } - default: - return state - } -} diff --git a/rdmo/projects/assets/js/interview/reducers/rootReducer.js b/rdmo/projects/assets/js/interview/reducers/rootReducer.js deleted file mode 100644 index b4866f767f..0000000000 --- a/rdmo/projects/assets/js/interview/reducers/rootReducer.js +++ /dev/null @@ -1,11 +0,0 @@ -import { combineReducers } from 'redux' - -import configReducer from './configReducer' -import pageReducer from './pageReducer' - -const rootReducer = combineReducers({ - config: configReducer, - page: pageReducer -}) - -export default rootReducer diff --git a/rdmo/projects/assets/js/interview/store/configureStore.js b/rdmo/projects/assets/js/interview/store/configureStore.js index f530eae6d3..e917b0c252 100644 --- a/rdmo/projects/assets/js/interview/store/configureStore.js +++ b/rdmo/projects/assets/js/interview/store/configureStore.js @@ -1,12 +1,15 @@ -import { applyMiddleware, createStore } from 'redux' +import { applyMiddleware, createStore, combineReducers } from 'redux' import Cookies from 'js-cookie' import thunk from 'redux-thunk' import isEmpty from 'lodash/isEmpty' -import rootReducer from '../reducers/rootReducer' +import configReducer from '../reducers/configReducer' +import interviewReducer from '../reducers/interviewReducer' import * as configActions from '../actions/configActions' -import * as pageActions from '../actions/pageActions' +import * as interviewActions from '../actions/interviewActions' + +import { parseLocation } from '../utils/location' export default function configureStore() { const middlewares = [thunk] @@ -25,23 +28,33 @@ export default function configureStore() { middlewares.push(logger) } + const rootReducer = combineReducers({ + config: configReducer, + interview: interviewReducer + }) + const store = createStore( rootReducer, applyMiddleware(...middlewares) ) - const fetchConfig = () => store.dispatch(configActions.fetchConfig()) - - const fetchPage = () => store.dispatch(pageActions.fetchPage()) + const fetchPageFromLocation = () => { + const { pageId } = parseLocation() + store.dispatch(interviewActions.fetchPage(pageId)) + } // this event is triggered when the page first loads window.addEventListener('load', () => { - fetchConfig().then(() => fetchPage()) + Promise.all([ + store.dispatch(configActions.fetchConfig()), + store.dispatch(interviewActions.fetchOverview()), + store.dispatch(interviewActions.fetchProgress()) + ]).then(() => fetchPageFromLocation()) }) // this event is triggered when when the forward/back buttons are used window.addEventListener('popstate', () => { - + fetchPageFromLocation() }) return store diff --git a/rdmo/projects/assets/js/interview/utils/location.js b/rdmo/projects/assets/js/interview/utils/location.js new file mode 100644 index 0000000000..d8556ec235 --- /dev/null +++ b/rdmo/projects/assets/js/interview/utils/location.js @@ -0,0 +1,26 @@ +import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' +import projectId from '../utils/projectId' + +const parseLocation = () => { + const pathname = window.location.pathname + + const m1 = pathname.match(/\/interview\/(?\d+)[/]*$/) + if (m1) { + return m1.groups + } + + return {} +} + +const updateLocation = (pageId) => { + const pathname = buildPath(pageId) + if (pathname != window.location.pathname) { + history.pushState(null, null, pathname) + } +} + +const buildPath = (pageId) => { + return `${baseUrl}/projects/${projectId}/interview/${pageId}/` +} + +export { parseLocation, updateLocation, buildPath } diff --git a/rdmo/projects/assets/js/interview/utils/projectId.js b/rdmo/projects/assets/js/interview/utils/projectId.js new file mode 100644 index 0000000000..4b642d4b1f --- /dev/null +++ b/rdmo/projects/assets/js/interview/utils/projectId.js @@ -0,0 +1,2 @@ +// take the baseurl from the of the django template +export default document.querySelector('meta[name="project"]').content.replace(/\/+$/, '') diff --git a/rdmo/projects/templates/projects/project_interview.html b/rdmo/projects/templates/projects/project_interview.html index 8c894b557d..c306464373 100644 --- a/rdmo/projects/templates/projects/project_interview.html +++ b/rdmo/projects/templates/projects/project_interview.html @@ -6,6 +6,10 @@ {% block vendor %} {% endblock %} +{% block head %} + +{% endblock %} + {% block css %} From c33032ff8c9fc37921a2b824a65e755edd100eb2 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 2 Apr 2024 16:10:26 +0200 Subject: [PATCH 07/72] Add TemplateAPI and help to sidebar in interview --- rdmo/core/settings.py | 7 ++++++- .../js/interview/actions/configActions.js | 5 +++-- .../assets/js/interview/components/Buttons.js | 9 +++++++-- .../js/interview/components/Navigation.js | 9 +++++++-- .../js/interview/components/Overview.js | 9 +++++++-- .../js/interview/components/Progress.js | 9 +++++++-- .../assets/js/interview/containers/Sidebar.js | 19 +++++++++++++++---- .../project_interview_buttons_help.html | 0 .../project_interview_navigation_help.html | 17 +++++++++++++++++ .../project_interview_overview_help.html | 0 .../project_interview_progress_help.html | 0 11 files changed, 69 insertions(+), 15 deletions(-) create mode 100644 rdmo/projects/templates/projects/project_interview_buttons_help.html create mode 100644 rdmo/projects/templates/projects/project_interview_navigation_help.html create mode 100644 rdmo/projects/templates/projects/project_interview_overview_help.html create mode 100644 rdmo/projects/templates/projects/project_interview_progress_help.html diff --git a/rdmo/core/settings.py b/rdmo/core/settings.py index 0e6add4dc8..ab08639c1c 100644 --- a/rdmo/core/settings.py +++ b/rdmo/core/settings.py @@ -222,7 +222,12 @@ 'PROJECT_TABLE_PAGE_SIZE' ] -TEMPLATES_API = [] +TEMPLATES_API = [ + 'projects/project_interview_buttons_help.html', + 'projects/project_interview_navigation_help.html', + 'projects/project_interview_overview_help.html', + 'projects/project_interview_progress_help.html', +] EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend' DEFAULT_FROM_EMAIL = 'info@example.com' diff --git a/rdmo/projects/assets/js/interview/actions/configActions.js b/rdmo/projects/assets/js/interview/actions/configActions.js index 1e19b0f9c3..d2b12ca0af 100644 --- a/rdmo/projects/assets/js/interview/actions/configActions.js +++ b/rdmo/projects/assets/js/interview/actions/configActions.js @@ -5,8 +5,9 @@ import { FETCH_CONFIG_SUCCESS, FETCH_CONFIG_ERROR, UPDATE_CONFIG } from './types export function fetchConfig() { return (dispatch) => Promise.all([ CoreApi.fetchSettings(), - ]).then(([settings]) => dispatch(fetchConfigSuccess({ - settings + CoreApi.fetchTemplates(), + ]).then(([settings, templates]) => dispatch(fetchConfigSuccess({ + settings, templates }))) } diff --git a/rdmo/projects/assets/js/interview/components/Buttons.js b/rdmo/projects/assets/js/interview/components/Buttons.js index b2581adfb7..382c0114a8 100644 --- a/rdmo/projects/assets/js/interview/components/Buttons.js +++ b/rdmo/projects/assets/js/interview/components/Buttons.js @@ -1,9 +1,13 @@ import React from 'react' import PropTypes from 'prop-types' -const Buttons = ({ page, onClick }) => { +const Buttons = ({ page, help, onClick }) => { return ( <> +
        +
        @@ -27,6 +31,7 @@ const Buttons = ({ page, onClick }) => { Buttons.propTypes = { page: PropTypes.object.isRequired, + help: PropTypes.string.isRequired, onClick: PropTypes.func.isRequired } diff --git a/rdmo/projects/assets/js/interview/components/Navigation.js b/rdmo/projects/assets/js/interview/components/Navigation.js index 6fba977089..43b6a5f8d0 100644 --- a/rdmo/projects/assets/js/interview/components/Navigation.js +++ b/rdmo/projects/assets/js/interview/components/Navigation.js @@ -2,7 +2,7 @@ import React from 'react' import PropTypes from 'prop-types' import classNames from 'classnames' -const Navigation = ({ page, navigation, onClick }) => { +const Navigation = ({ page, navigation, help, onClick }) => { const handleClick = (event, pageId) => { event.preventDefault() @@ -13,6 +13,10 @@ const Navigation = ({ page, navigation, onClick }) => { <>

        {gettext('Navigation')}

        +
        +
          { navigation.map((s, sIndex) => ( @@ -33,7 +37,7 @@ const Navigation = ({ page, navigation, onClick }) => { { p.count > 0 && p.count == p.total && ( - + {' '} ) } @@ -65,6 +69,7 @@ const Navigation = ({ page, navigation, onClick }) => { Navigation.propTypes = { page: PropTypes.object.isRequired, navigation: PropTypes.array.isRequired, + help: PropTypes.string.isRequired, onClick: PropTypes.func.isRequired } diff --git a/rdmo/projects/assets/js/interview/components/Overview.js b/rdmo/projects/assets/js/interview/components/Overview.js index 6b4b3d3bcd..c269095a66 100644 --- a/rdmo/projects/assets/js/interview/components/Overview.js +++ b/rdmo/projects/assets/js/interview/components/Overview.js @@ -3,7 +3,7 @@ import PropTypes from 'prop-types' import baseUrl from 'rdmo/core/assets/js/utils/baseUrl' -const Overview = ({ overview }) => { +const Overview = ({ overview, help }) => { const projectsUrl = `${baseUrl}/projects/` const projectUrl = `${baseUrl}/projects/${overview.id}` @@ -12,6 +12,10 @@ const Overview = ({ overview }) => { <>

          {gettext('Overview')}

          +
          +
          • @@ -37,7 +41,8 @@ const Overview = ({ overview }) => { } Overview.propTypes = { - overview: PropTypes.object.isRequired + overview: PropTypes.object.isRequired, + help: PropTypes.string.isRequired } export default Overview diff --git a/rdmo/projects/assets/js/interview/components/Progress.js b/rdmo/projects/assets/js/interview/components/Progress.js index 59de5e33d2..d445d214fc 100644 --- a/rdmo/projects/assets/js/interview/components/Progress.js +++ b/rdmo/projects/assets/js/interview/components/Progress.js @@ -1,7 +1,7 @@ import React from 'react' import PropTypes from 'prop-types' -const Progress = ({ progress }) => { +const Progress = ({ progress, help }) => { const low = progress.ratio <= 0.25 const width = progress.ratio * 100 const label = interpolate(gettext('%s of %s'), [progress.count, progress.total]) @@ -10,6 +10,10 @@ const Progress = ({ progress }) => { <>

            {gettext('Progress')}

            +
            +
            {low &&
            } @@ -24,7 +28,8 @@ const Progress = ({ progress }) => { } Progress.propTypes = { - progress: PropTypes.object.isRequired + progress: PropTypes.object.isRequired, + help: PropTypes.string.isRequired } export default Progress diff --git a/rdmo/projects/assets/js/interview/containers/Sidebar.js b/rdmo/projects/assets/js/interview/containers/Sidebar.js index 17b5538c3e..fc18172bdb 100644 --- a/rdmo/projects/assets/js/interview/containers/Sidebar.js +++ b/rdmo/projects/assets/js/interview/containers/Sidebar.js @@ -16,10 +16,21 @@ const Sidebar = ({ config, interview, configActions, interviewActions }) => { if (interview.show) { return (
            - - - - + + + +
            ) } diff --git a/rdmo/projects/templates/projects/project_interview_buttons_help.html b/rdmo/projects/templates/projects/project_interview_buttons_help.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rdmo/projects/templates/projects/project_interview_navigation_help.html b/rdmo/projects/templates/projects/project_interview_navigation_help.html new file mode 100644 index 0000000000..60abef29bf --- /dev/null +++ b/rdmo/projects/templates/projects/project_interview_navigation_help.html @@ -0,0 +1,17 @@ +{% load i18n %} + +{% if settings.PROJECT_QUESTIONS_AUTOSAVE %} +

            + {% trans 'Using the navigation will save your input.' %} +

            +{% else %} +

            + {% trans 'Please note that using the navigation will discard any unsaved input.' %} +

            +{% endif %} + +

            +{% blocktrans trimmed %} +Grey entries will be conditionally skipped based on your input. +{% endblocktrans %} +

            diff --git a/rdmo/projects/templates/projects/project_interview_overview_help.html b/rdmo/projects/templates/projects/project_interview_overview_help.html new file mode 100644 index 0000000000..e69de29bb2 diff --git a/rdmo/projects/templates/projects/project_interview_progress_help.html b/rdmo/projects/templates/projects/project_interview_progress_help.html new file mode 100644 index 0000000000..e69de29bb2 From 3ebb76876b4fa1ec896dd2e653c80b209873ab7e Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Tue, 2 Apr 2024 16:55:03 +0200 Subject: [PATCH 08/72] Add Page, Question, QuestionSet and widgets components --- .../assets/js/interview/components/Page.js | 32 ++++++++++++++ .../js/interview/components/Question.js | 18 ++++++++ .../js/interview/components/QuestionSet.js | 18 ++++++++ .../assets/js/interview/components/Widget.js | 44 +++++++++++++++++++ .../components/widgets/Autocomplete.js | 18 ++++++++ .../interview/components/widgets/Checkbox.js | 18 ++++++++ .../js/interview/components/widgets/Date.js | 18 ++++++++ .../js/interview/components/widgets/File.js | 18 ++++++++ .../js/interview/components/widgets/Radio.js | 18 ++++++++ .../js/interview/components/widgets/Range.js | 18 ++++++++ .../js/interview/components/widgets/Select.js | 18 ++++++++ .../js/interview/components/widgets/Text.js | 18 ++++++++ .../interview/components/widgets/Textarea.js | 18 ++++++++ .../js/interview/components/widgets/YesNo.js | 18 ++++++++ .../assets/js/interview/containers/Main.js | 6 +-- 15 files changed, 295 insertions(+), 3 deletions(-) create mode 100644 rdmo/projects/assets/js/interview/components/Page.js create mode 100644 rdmo/projects/assets/js/interview/components/Question.js create mode 100644 rdmo/projects/assets/js/interview/components/QuestionSet.js create mode 100644 rdmo/projects/assets/js/interview/components/Widget.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/Autocomplete.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/Checkbox.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/Date.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/File.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/Radio.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/Range.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/Select.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/Text.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/Textarea.js create mode 100644 rdmo/projects/assets/js/interview/components/widgets/YesNo.js diff --git a/rdmo/projects/assets/js/interview/components/Page.js b/rdmo/projects/assets/js/interview/components/Page.js new file mode 100644 index 0000000000..ea5f619dd1 --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/Page.js @@ -0,0 +1,32 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Question from '../components/Question' +import QuestionSet from '../components/Question' + +const Page = ({ page }) => { + return ( +
            +

            + {page.title} +

            + { + page.elements.map((element, elementIndex) => { + if (element.model == 'questions.questionset') { + return + } else { + return + } + }) + } +
            + ) +} + +Page.propTypes = { + page: PropTypes.object.isRequired +} + +export default Page diff --git a/rdmo/projects/assets/js/interview/components/Question.js b/rdmo/projects/assets/js/interview/components/Question.js new file mode 100644 index 0000000000..b9211de072 --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/Question.js @@ -0,0 +1,18 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Widget from './Widget' + +const Question = ({ question }) => { + return ( +
            + +
            + ) +} + +Question.propTypes = { + question: PropTypes.object.isRequired +} + +export default Question diff --git a/rdmo/projects/assets/js/interview/components/QuestionSet.js b/rdmo/projects/assets/js/interview/components/QuestionSet.js new file mode 100644 index 0000000000..132c9a9d84 --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/QuestionSet.js @@ -0,0 +1,18 @@ +import React from 'react' +import PropTypes from 'prop-types' + +const QuestionSet = ({ questionset }) => { + console.log(questionset) + + return ( +
            + +
            + ) +} + +QuestionSet.propTypes = { + questionset: PropTypes.object.isRequired +} + +export default QuestionSet diff --git a/rdmo/projects/assets/js/interview/components/Widget.js b/rdmo/projects/assets/js/interview/components/Widget.js new file mode 100644 index 0000000000..bcc9f15cac --- /dev/null +++ b/rdmo/projects/assets/js/interview/components/Widget.js @@ -0,0 +1,44 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import Autocomplete from './widgets/Autocomplete' +import Checkbox from './widgets/Checkbox' +import Date from './widgets/Date' +import File from './widgets/File' +import Radio from './widgets/Radio' +import Range from './widgets/Range' +import Select from './widgets/Select' +import Text from './widgets/Text' +import Textarea from './widgets/Textarea' +import YesNo from './widgets/YesNo' + +const Widget = ({ question }) => { + switch (question.widget_type) { + case 'autocomplete': + return + case 'checkbox': + return + case 'date': + return + case 'file': + return + case 'radio': + return + case 'range': + return + case 'select': + return