From 1e97f24e81ac758686c1debbe1d1a3faa094d916 Mon Sep 17 00:00:00 2001 From: Jochen Klar Date: Mon, 19 Aug 2024 18:30:21 +0200 Subject: [PATCH] Add copySet action to interview --- .../js/interview/actions/actionTypes.js | 4 + .../js/interview/actions/interviewActions.js | 88 +++++++++++++++++-- .../js/interview/components/main/page/Page.js | 17 ++-- .../components/main/page/PageHead.js | 43 +++++++-- .../components/main/page/PageHeadFormModal.js | 6 +- .../assets/js/interview/containers/Main.js | 1 + .../js/interview/reducers/interviewReducer.js | 10 ++- .../projects/assets/js/interview/utils/set.js | 6 +- 8 files changed, 151 insertions(+), 24 deletions(-) diff --git a/rdmo/projects/assets/js/interview/actions/actionTypes.js b/rdmo/projects/assets/js/interview/actions/actionTypes.js index d8025bf3ad..b42995cdbf 100644 --- a/rdmo/projects/assets/js/interview/actions/actionTypes.js +++ b/rdmo/projects/assets/js/interview/actions/actionTypes.js @@ -50,3 +50,7 @@ export const CREATE_SET = 'CREATE_SET' export const DELETE_SET_INIT = 'DELETE_SET_INIT' export const DELETE_SET_SUCCESS = 'DELETE_SET_SUCCESS' export const DELETE_SET_ERROR = 'DELETE_SET_ERROR' + +export const COPY_SET_INIT = 'COPY_SET_INIT' +export const COPY_SET_SUCCESS = 'COPY_SET_SUCCESS' +export const COPY_SET_ERROR = 'COPY_SET_ERROR' diff --git a/rdmo/projects/assets/js/interview/actions/interviewActions.js b/rdmo/projects/assets/js/interview/actions/interviewActions.js index d9ba9cf468..3053b803b7 100644 --- a/rdmo/projects/assets/js/interview/actions/interviewActions.js +++ b/rdmo/projects/assets/js/interview/actions/interviewActions.js @@ -47,7 +47,10 @@ import { CREATE_SET, DELETE_SET_INIT, DELETE_SET_SUCCESS, - DELETE_SET_ERROR + DELETE_SET_ERROR, + COPY_SET_INIT, + COPY_SET_SUCCESS, + COPY_SET_ERROR } from './actionTypes' import { updateConfig } from 'rdmo/core/assets/js/actions/configActions' @@ -459,8 +462,8 @@ export function createSet(attrs) { // create a value for the text if the page has an attribute const value = isNil(attrs.attribute) ? null : ValueFactory.create(attrs) - // create an action to be called immediately or after saving the value - const createSetSuccess = (value) => { + // create a callback function to be called immediately or after saving the value + const createSetCallback = (value) => { dispatch(activateSet(set)) const state = getState().interview @@ -476,12 +479,12 @@ export function createSet(attrs) { } if (isNil(value)) { - return createSetSuccess() + return createSetCallback() } else { return dispatch(storeValue(value)).then(() => { const storedValue = getState().interview.values.find((v) => compareValues(v, value)) if (!isNil(storedValue)) { - createSetSuccess(storedValue) + createSetCallback(storedValue) } }) } @@ -559,3 +562,78 @@ export function deleteSetSuccess(set) { export function deleteSetError(errors) { return {type: DELETE_SET_ERROR, errors} } + +export function copySet(currentSet, attrs) { + const pendingId = `copySet/${currentSet.set_prefix}/${currentSet.set_index}` + + return (dispatch, getState) => { + dispatch(addToPending(pendingId)) + dispatch(copySetInit()) + + // create a new set + const set = SetFactory.create(attrs) + + // create a value for the text if the page has an attribute + const value = isNil(attrs.attribute) ? null : ValueFactory.create(attrs) + + // create a callback function to be called immediately or after saving the value + const copySetCallback = (setValues) => { + dispatch(activateSet(set)) + + const state = getState().interview + + const page = state.page + const values = [...state.values, ...setValues] + const sets = gatherSets(values) + + initSets(sets, page) + initValues(sets, values, page) + + return dispatch({type: COPY_SET_SUCCESS, values, sets}) + } + + if (isNil(value)) { + // gather all values for the currentSet and it's descendants + const currentValues = getDescendants(getState().interview.values, currentSet) + + // store each value in currentSet with the new set_index + return Promise.all( + currentValues.filter((currentValue) => !isEmptyValue(currentValue)).map((currentValue) => { + const value = {...currentValue} + const setPrefixLength = set.set_prefix.split('|').length + + if (value.set_prefix == set.set_prefix) { + value.set_index == set.set_index + } else { + value.set_prefix = value.set_prefix.split('|').reduce((acc, cur, idx) => { + return [...acc, (idx == setPrefixLength - 1) ? set.set_index : cur] + }, []).join('|') + } + + delete value.id + return ValueApi.storeValue(projectId, value) + }) + ).then((values) => { + dispatch(removeFromPending(pendingId)) + dispatch(copySetCallback(values)) + }).catch((errors) => { + dispatch(removeFromPending(pendingId)) + dispatch(copySetError(errors)) + }) + } else { + console.log(value) + } + } +} + +export function copySetInit() { + return {type: COPY_SET_INIT} +} + +export function copySetSuccess(values, sets) { + return {type: COPY_SET_SUCCESS, values, sets} +} + +export function copySetError(errors) { + return {type: COPY_SET_ERROR, errors} +} diff --git a/rdmo/projects/assets/js/interview/components/main/page/Page.js b/rdmo/projects/assets/js/interview/components/main/page/Page.js index dcab494514..f55861894b 100644 --- a/rdmo/projects/assets/js/interview/components/main/page/Page.js +++ b/rdmo/projects/assets/js/interview/components/main/page/Page.js @@ -13,12 +13,17 @@ import PageHead from './PageHead' const Page = ({ config, templates, overview, page, sets, values, fetchPage, createValue, updateValue, deleteValue, copyValue, - activateSet, createSet, updateSet, deleteSet }) => { + activateSet, createSet, updateSet, deleteSet, copySet }) => { const currentSetPrefix = '' - const currentSetIndex = page.is_collection ? get(config, 'page.currentSetIndex', 0) : 0 - const currentSet = sets.find((set) => (set.set_prefix == currentSetPrefix && set.set_index == currentSetIndex)) || - sets.find((set) => (set.set_prefix == currentSetPrefix && set.set_index == 0)) // sanity check + let currentSetIndex = page.is_collection ? get(config, 'page.currentSetIndex', 0) : 0 + let currentSet = sets.find((set) => (set.set_prefix == currentSetPrefix && set.set_index == currentSetIndex)) + + // sanity check + if (isNil(currentSet)) { + currentSetIndex = 0 + currentSet = sets.find((set) => (set.set_prefix == currentSetPrefix && set.set_index == 0)) + } const isManager = (overview.is_superuser || overview.is_editor || overview.is_reviewer) @@ -36,6 +41,7 @@ const Page = ({ config, templates, overview, page, sets, values, fetchPage, createSet={createSet} updateSet={updateSet} deleteSet={deleteSet} + copySet={copySet} />
{ @@ -108,11 +114,12 @@ Page.propTypes = { createValue: PropTypes.func.isRequired, updateValue: PropTypes.func.isRequired, deleteValue: PropTypes.func.isRequired, + copyValue: PropTypes.func.isRequired, activateSet: PropTypes.func.isRequired, createSet: PropTypes.func.isRequired, updateSet: PropTypes.func.isRequired, deleteSet: PropTypes.func.isRequired, - copyValue: PropTypes.func.isRequired + copySet: PropTypes.func.isRequired } export default Page diff --git a/rdmo/projects/assets/js/interview/components/main/page/PageHead.js b/rdmo/projects/assets/js/interview/components/main/page/PageHead.js index 87371c5ecf..3087be1fc1 100644 --- a/rdmo/projects/assets/js/interview/components/main/page/PageHead.js +++ b/rdmo/projects/assets/js/interview/components/main/page/PageHead.js @@ -9,7 +9,8 @@ import useModal from 'rdmo/core/assets/js/hooks/useModal' import PageHeadDeleteModal from './PageHeadDeleteModal' import PageHeadFormModal from './PageHeadFormModal' -const PageHead = ({ templates, page, sets, values, currentSet, activateSet, createSet, updateSet, deleteSet }) => { +const PageHead = ({ templates, page, sets, values, currentSet, + activateSet, createSet, updateSet, deleteSet, copySet }) => { const currentSetValue = isNil(currentSet) ? null : ( values.find((value) => ( @@ -20,6 +21,7 @@ const PageHead = ({ templates, page, sets, values, currentSet, activateSet, crea const [showCreateModal, openCreateModal, closeCreateModal] = useModal() const [showUpdateModal, openUpdateModal, closeUpdateModal] = useModal() const [showDeleteModal, openDeleteModal, closeDeleteModal] = useModal() + const [showCopyModal, openCopyModal, closeCopyModal] = useModal() const handleActivateSet = (event, set) => { event.preventDefault() @@ -53,6 +55,16 @@ const PageHead = ({ templates, page, sets, values, currentSet, activateSet, crea closeDeleteModal() } + const handleCopySet = (text) => { + copySet(currentSet, { + attribute: page.attribute, + set_index: last(sets) ? last(sets).set_index + 1 : 0, + set_collection: page.is_collection, + text + }) + closeCopyModal() + } + return page.is_collection && (
@@ -82,12 +94,13 @@ const PageHead = ({ templates, page, sets, values, currentSet, activateSet, crea
- { - page.attribute && ( -
) : ( @@ -99,15 +112,28 @@ const PageHead = ({ templates, page, sets, values, currentSet, activateSet, crea + { currentSetValue && ( { +const PageHeadFormModal = ({ title, submitText, submitColor, show, initial, onClose, onSubmit }) => { const ref = useRef(null) const [inputValue, setInputValue] = useState('') const [hasError, setHasError] = useState(false) - const submitText = isEmpty(initial) ? gettext('Create') : gettext('Update') - const submitColor = isEmpty(initial) ? 'success' : 'primary' const handleSubmit = () => { if (isEmpty(inputValue) && !isNil(initial)) { @@ -77,6 +75,8 @@ const PageHeadFormModal = ({ title, show, initial, onClose, onSubmit }) => { PageHeadFormModal.propTypes = { title: PropTypes.string.isRequired, + submitText: PropTypes.string.isRequired, + submitColor: PropTypes.string.isRequired, show: PropTypes.bool.isRequired, initial: PropTypes.string, onClose: PropTypes.func.isRequired, diff --git a/rdmo/projects/assets/js/interview/containers/Main.js b/rdmo/projects/assets/js/interview/containers/Main.js index a8f6189fe2..be0b98f316 100644 --- a/rdmo/projects/assets/js/interview/containers/Main.js +++ b/rdmo/projects/assets/js/interview/containers/Main.js @@ -54,6 +54,7 @@ const Main = ({ config, settings, templates, user, project, interview, configAct updateSet={interviewActions.updateSet} deleteSet={interviewActions.deleteSet} copyValue={interviewActions.copyValue} + copySet={interviewActions.copySet} /> ) } diff --git a/rdmo/projects/assets/js/interview/reducers/interviewReducer.js b/rdmo/projects/assets/js/interview/reducers/interviewReducer.js index 39ac156bc2..d62bcd60cf 100644 --- a/rdmo/projects/assets/js/interview/reducers/interviewReducer.js +++ b/rdmo/projects/assets/js/interview/reducers/interviewReducer.js @@ -25,7 +25,10 @@ import { CREATE_SET, DELETE_SET_INIT, DELETE_SET_SUCCESS, - DELETE_SET_ERROR + DELETE_SET_ERROR, + COPY_SET_INIT, + COPY_SET_SUCCESS, + COPY_SET_ERROR } from '../actions/actionTypes' const initialState = { @@ -76,6 +79,8 @@ export default function interviewReducer(state = initialState, action) { values: state.values.filter((value) => !action.values.includes(value)), sets: state.sets.filter((set) => !action.sets.includes(set)) } + case COPY_SET_SUCCESS: + return { ...state, values: action.values, sets: action.sets } case FETCH_PAGE_INIT: case FETCH_NAVIGATION_INIT: case FETCH_OPTIONS_INIT: @@ -97,6 +102,7 @@ export default function interviewReducer(state = initialState, action) { )) } case DELETE_SET_INIT: + case COPY_SET_INIT: return { ...state, errors: [] } case FETCH_PAGE_ERROR: case FETCH_NAVIGATION_ERROR: @@ -117,6 +123,8 @@ export default function interviewReducer(state = initialState, action) { case DELETE_VALUE_ERROR: case DELETE_SET_ERROR: return { ...state, errors: [...state.errors, { actionType: action.type, ...action.error }] } + case COPY_SET_ERROR: + return { ...state, errors: [...state.errors, { actionType: action.type, ...action.error }] } default: return state } diff --git a/rdmo/projects/assets/js/interview/utils/set.js b/rdmo/projects/assets/js/interview/utils/set.js index 282fa90b21..9a576875f9 100644 --- a/rdmo/projects/assets/js/interview/utils/set.js +++ b/rdmo/projects/assets/js/interview/utils/set.js @@ -1,4 +1,4 @@ -import { isEmpty, isNil, toNumber, toString, last } from 'lodash' +import { isEmpty, isNil, toNumber, toString, last, sortBy } from 'lodash' import SetFactory from '../factories/SetFactory' @@ -27,7 +27,7 @@ const getDescendants = (items, set) => { } const gatherSets = (values) => { - return values.reduce((sets, value) => { + const sets = values.reduce((sets, value) => { if (sets.find((set) => ( (set.set_prefix === value.set_prefix) && (set.set_index === value.set_index) @@ -40,6 +40,8 @@ const gatherSets = (values) => { })] } }, []) + + return sortBy(sets, ['set_prefix', 'set_index']) } const initSets = (sets, element, setPrefix) => {