Skip to content

Commit 4579339

Browse files
authored
Merge pull request #1188 from rdmorganiser/interview-import-dataset
feat(interview): Reuse datasets and values [5]
2 parents ad6b993 + 2a95493 commit 4579339

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

48 files changed

+1476
-299
lines changed

Diff for: .gitignore

+16-14
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
__pycache__
2+
*.pyc
3+
*~
4+
*.swp
5+
.DS_Store
6+
17
testing/config/settings/local.py
28
testing/log/
39
.pytest*/
@@ -6,19 +12,12 @@ static_root
612
media_root
713
components_root
814

9-
docs/_build*
10-
11-
__pycache__
12-
*.pyc
13-
*~
14-
*.swp
15-
.DS_Store
16-
.ruff_cache
17-
1815
env
1916
env2
2017
env3
2118

19+
Makefile
20+
2221
.coverage
2322
.coverage.*
2423
htmlcov
@@ -32,16 +31,19 @@ dist
3231
.imdone/
3332

3433
.pytest_cache
34+
.ruff_cache
3535
.on-save.json
3636

3737
rdmo/management/static
3838

39-
rdmo/core/static/core/js/base*.js
39+
rdmo/core/static/core/js/base.js
40+
rdmo/core/static/core/css/base.css
4041
rdmo/core/static/core/fonts
41-
rdmo/core/static/core/css/base*.css
4242

43-
rdmo/projects/static/projects/js/*.js
44-
rdmo/projects/static/projects/fonts
45-
rdmo/projects/static/projects/css/*.css
43+
rdmo/projects/static/projects/css/interview.css
44+
rdmo/projects/static/projects/css/projects.css
45+
rdmo/projects/static/projects/fonts/
46+
rdmo/projects/static/projects/js/interview.js
47+
rdmo/projects/static/projects/js/projects.js
4648

4749
screenshots

Diff for: rdmo/core/assets/js/components/Modal.js

+4-2
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react'
22
import PropTypes from 'prop-types'
33
import { Modal as BootstrapModal } from 'react-bootstrap'
44

5-
const Modal = ({ title, show, modalProps, submitLabel, submitProps, onClose, onSubmit, children }) => {
5+
const Modal = ({ title, show, modalProps, submitLabel, submitProps, onClose, onSubmit, children, buttons }) => {
66
return (
77
<BootstrapModal className="element-modal" onHide={onClose} show={show} {...modalProps}>
88
<BootstrapModal.Header closeButton>
@@ -19,6 +19,7 @@ const Modal = ({ title, show, modalProps, submitLabel, submitProps, onClose, onS
1919
<button type="button" className="btn btn-default" onClick={onClose}>
2020
{gettext('Close')}
2121
</button>
22+
{buttons}
2223
{
2324
onSubmit && (
2425
<button type="button" className="btn btn-primary" onClick={onSubmit} {...submitProps}>
@@ -39,7 +40,8 @@ Modal.propTypes = {
3940
submitProps: PropTypes.object,
4041
onClose: PropTypes.func.isRequired,
4142
onSubmit: PropTypes.func,
42-
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]).isRequired,
43+
children: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
44+
buttons: PropTypes.oneOfType([PropTypes.arrayOf(PropTypes.node), PropTypes.node]),
4345
}
4446

4547
export default Modal

Diff for: rdmo/core/assets/js/hooks/useLsState.js

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
import { useState } from 'react'
2+
import { isEmpty, isPlainObject, omit as lodashOmit } from 'lodash'
3+
4+
import { checkStoreId } from '../utils/store'
5+
6+
const useLsState = (path, initialValue, omit = []) => {
7+
checkStoreId()
8+
9+
const getLs = (path) => {
10+
const data = JSON.parse(localStorage.getItem(path))
11+
return isPlainObject(data) ? lodashOmit(data, omit) : data
12+
}
13+
14+
const setLs = (path, value) => {
15+
const data = isPlainObject(value) ? lodashOmit(value, omit) : value
16+
localStorage.setItem(path, JSON.stringify(data))
17+
}
18+
19+
const getInitialState = () => {
20+
// get the value from the local storage
21+
const lsValue = getLs(path)
22+
23+
// return the state with the value from the local storage or the provided initialValue
24+
if (isPlainObject(lsValue)) {
25+
return { ...initialValue, ...lsValue }
26+
} else if (isEmpty(lsValue)) {
27+
return initialValue
28+
} else {
29+
return lsValue
30+
}
31+
}
32+
33+
// setup the state
34+
const [value, setValue] = useState(getInitialState())
35+
36+
return [
37+
value,
38+
(value) => {
39+
setLs(path, value)
40+
setValue(value)
41+
},
42+
() => setValue(getInitialState())
43+
]
44+
}
45+
46+
export default useLsState

Diff for: rdmo/core/assets/scss/style.scss

+6-2
Original file line numberDiff line numberDiff line change
@@ -190,8 +190,7 @@ metadata {
190190
.page h2:nth-child(2) {
191191
margin-top: 0;
192192
}
193-
.sidebar h2:first-child,
194-
.sidebar .import-buttons {
193+
.sidebar > *:first-child {
195194
margin-top: 70px;
196195
}
197196

@@ -317,6 +316,7 @@ form .yesno label {
317316
/* modals */
318317

319318
.modal-body {
319+
> div:last-child,
320320
> p:last-child,
321321
formgroup:last-child .form-group {
322322
margin-bottom: 0;
@@ -495,3 +495,7 @@ li.has-warning > a.control-label > i {
495495
color: $link-color;
496496
cursor: pointer;
497497
}
498+
499+
.has-error .react-select__control {
500+
border-color: #a94442;
501+
}

Diff for: rdmo/core/settings.py

+4-1
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,8 @@
238238
'projects/project_interview_page_tabs_help.html',
239239
'projects/project_interview_progress_help.html',
240240
'projects/project_interview_question_help.html',
241-
'projects/project_interview_questionset_help.html'
241+
'projects/project_interview_questionset_help.html',
242+
'projects/project_interview_sidebar.html',
242243
]
243244

244245
EMAIL_BACKEND = 'django.core.mail.backends.console.EmailBackend'
@@ -348,6 +349,8 @@
348349

349350
OPTIONSET_PROVIDERS = []
350351

352+
PROJECT_VALUES_SEARCH_LIMIT = 10
353+
351354
PROJECT_VALUES_VALIDATION = False
352355

353356
PROJECT_VALUES_VALIDATION_URL = True

Diff for: rdmo/projects/assets/js/interview/actions/interviewActions.js

+13-8
Original file line numberDiff line numberDiff line change
@@ -611,18 +611,20 @@ export function deleteSetError(errors) {
611611
return {type: DELETE_SET_ERROR, errors}
612612
}
613613

614-
export function copySet(currentSet, currentSetValue, attrs) {
615-
const pendingId = `copySet/${currentSet.set_prefix}/${currentSet.set_index}`
614+
export function copySet(currentSet, copySetValue, attrs) {
615+
const pendingId = isNil(currentSet) ? 'copySet' : `copySet/${currentSet.set_prefix}/${currentSet.set_index}`
616616

617617
return (dispatch, getState) => {
618618
dispatch(addToPending(pendingId))
619619
dispatch(copySetInit())
620620

621-
// create a new set
622-
const set = SetFactory.create(attrs)
621+
// create a new set (create) or use the current one (import)
622+
const set = isNil(attrs.id) ? SetFactory.create(attrs) : currentSet
623623

624-
// create a value for the text if the page has an attribute
625-
const value = isNil(attrs.attribute) ? null : ValueFactory.create(attrs)
624+
// create a value for the text if the page has an attribute (create) or use the current one (import)
625+
const value = isNil(attrs.attribute) ? null : (
626+
isNil(attrs.id) ? ValueFactory.create(attrs) : attrs
627+
)
626628

627629
// create a callback function to be called immediately or after saving the value
628630
const copySetCallback = (setValues) => {
@@ -631,7 +633,10 @@ export function copySet(currentSet, currentSetValue, attrs) {
631633
const state = getState().interview
632634

633635
const page = state.page
634-
const values = [...state.values, ...setValues]
636+
const values = [
637+
...state.values.filter(v => !setValues.some(sv => compareValues(v, sv))), // remove updated values
638+
...setValues
639+
]
635640
const sets = gatherSets(values)
636641

637642
initSets(sets, page)
@@ -667,7 +672,7 @@ export function copySet(currentSet, currentSetValue, attrs) {
667672
})
668673
)
669674
} else {
670-
promise = ValueApi.copySet(projectId, currentSetValue, value)
675+
promise = ValueApi.copySet(projectId, value, copySetValue)
671676
}
672677

673678
return promise.then((values) => {

Diff for: rdmo/projects/assets/js/interview/api/ProjectApi.js

+4
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,10 @@ import BaseApi from 'rdmo/core/assets/js/api/BaseApi'
66

77
class ProjectsApi extends BaseApi {
88

9+
static fetchProjects(params) {
10+
return this.get(`/api/v1/projects/projects/?${encodeParams(params)}`)
11+
}
12+
913
static fetchOverview(projectId) {
1014
return this.get(`/api/v1/projects/projects/${projectId}/overview/`)
1115
}

Diff for: rdmo/projects/assets/js/interview/api/ValueApi.js

+9-3
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import BaseApi from 'rdmo/core/assets/js/api/BaseApi'
22
import { encodeParams } from 'rdmo/core/assets/js/utils/api'
3-
import isUndefined from 'lodash/isUndefined'
3+
import { isUndefined } from 'lodash'
44

55
class ValueApi extends BaseApi {
66

77
static fetchValues(projectId, params) {
88
return this.get(`/api/v1/projects/projects/${projectId}/values/?${encodeParams(params)}`)
99
}
1010

11+
static searchValues(params) {
12+
return this.get(`/api/v1/projects/values/search/?${encodeParams(params)}`)
13+
}
14+
1115
static storeValue(projectId, value) {
1216
if (isUndefined(value.id)) {
1317
return this.post(`/api/v1/projects/projects/${projectId}/values/`, value)
@@ -29,8 +33,10 @@ class ValueApi extends BaseApi {
2933
}
3034
}
3135

32-
static copySet(projectId, currentSetValue, setValue) {
33-
return this.post(`/api/v1/projects/projects/${projectId}/values/${currentSetValue.id}/set/`, setValue)
36+
static copySet(projectId, setValue, copySetValue) {
37+
return this.post(`/api/v1/projects/projects/${projectId}/values/set/`, {
38+
...setValue, copy_set_value: copySetValue.id
39+
})
3440
}
3541

3642
static deleteSet(projectId, setValue) {

Diff for: rdmo/projects/assets/js/interview/components/main/Done.js

+1-2
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,7 @@ const Done = ({ templates }) => {
2828
}
2929

3030
Done.propTypes = {
31-
templates: PropTypes.object.isRequired,
32-
overview: PropTypes.object.isRequired
31+
templates: PropTypes.object.isRequired
3332
}
3433

3534
export default Done

0 commit comments

Comments
 (0)