diff --git a/client/components/download-link/renderers/docx-renderer.js b/client/components/download-link/renderers/docx-renderer.js index 35e78a3f..2c53e2b6 100644 --- a/client/components/download-link/renderers/docx-renderer.js +++ b/client/components/download-link/renderers/docx-renderer.js @@ -800,7 +800,8 @@ export default (application, sections, values, updateImageDimensions) => { switch (name) { case 'steps': - const [ steps ] = hydrateSteps(project.protocols, values.steps, project.reusableSteps || {}); + let [ steps ] = hydrateSteps(project.protocols, values.steps, project.reusableSteps || {}); + steps = steps.filter(step => !step.deleted); return (steps || []).forEach((stepValues, index) => { doc.createParagraph(`Step ${index + 1}${stepValues.reference ? `: ${stepValues.reference}` : ''} (${stepValues.optional ? 'optional' : 'mandatory'})`).heading4(); const repeatedFrom = getRepeatedFromProtocolIndex(stepValues, values.id); diff --git a/client/components/repeater.js b/client/components/repeater.js index 7cfb23d6..c0c833da 100644 --- a/client/components/repeater.js +++ b/client/components/repeater.js @@ -88,7 +88,9 @@ class Repeater extends Component { return Promise.resolve() .then(this.props.onBeforeRemove) .then(() => { - if (this.props.softDelete) { + // mark record deleted, second check is to + // ensure that reusable step which was used to create reusable step in a protocol is not deleted, when deleting an instance of reusable step. + if (this.props.softDelete && !this.state.items[index].reusable) { return this.update(this.state.items.map((item, i) => { if (index === i) { return { ...item, deleted: true }; diff --git a/client/helpers/steps.js b/client/helpers/steps.js index f5e11854..811bca68 100644 --- a/client/helpers/steps.js +++ b/client/helpers/steps.js @@ -36,6 +36,39 @@ export const hydrateSteps = (protocols, steps, reusableSteps) => { return [hydratedSteps, Object.values(reusableSteps)]; }; +export const removeNewDeleted = (steps, previousSteps) => { + let oldSteps = []; + previousSteps.forEach(protocol => { + protocol.forEach(step => oldSteps.push(step.id)); + }); + return (steps || []).filter(p => { + if (p.deleted === true) { + return !!oldSteps.includes(p.id); + } + return true; + }); +}; + +export const addDeletedReusableSteps = (steps, previousSteps, reusableSteps) => { + let stepIds = []; + steps.forEach(step => { + stepIds.push(step.id); + }); + let oldIndex = 0; + for (let i = 0; i < previousSteps.length; i++) { + if (stepIds.includes(previousSteps[i].id)) { + oldIndex = stepIds.indexOf(previousSteps[i].id); + } else { + oldIndex = oldIndex + 1; + const found = reusableSteps.find((reusableStep) => reusableStep.id === previousSteps[i].reusableStepId); + let step = {...found}; + step.deleted = true; + steps.splice(oldIndex, 0, step); + } + } + return steps; +}; + export const getTruncatedStepTitle = (step, numCharacters) => { const title = getStepTitle(step.title, null); if (!title || title.trim() === '') return null; diff --git a/client/pages/sections/granted/protocol-steps.js b/client/pages/sections/granted/protocol-steps.js index 72b8835c..4004fcf7 100644 --- a/client/pages/sections/granted/protocol-steps.js +++ b/client/pages/sections/granted/protocol-steps.js @@ -43,7 +43,8 @@ const Step = ({ id, index, fields, prefix, ...props }) => { }; const Steps = ({ values, fields, pdf, prefix, project }) => { - const [ steps ] = hydrateSteps(project.protocols, values.steps, project.reusableSteps || {}); + let [ steps ] = hydrateSteps(project.protocols, values.steps, project.reusableSteps || {}); + steps = steps.filter(step => !step.deleted); return (
diff --git a/client/pages/sections/protocols/steps.js b/client/pages/sections/protocols/steps.js index 0e5e5008..f6b52739 100644 --- a/client/pages/sections/protocols/steps.js +++ b/client/pages/sections/protocols/steps.js @@ -18,7 +18,9 @@ import { getRepeatedFromProtocolIndex, getStepTitle, getTruncatedStepTitle, - hydrateSteps + hydrateSteps, + removeNewDeleted, + addDeletedReusableSteps } from '../../../helpers/steps'; import { saveReusableSteps } from '../../../actions/projects'; import Expandable from '../../../components/expandable'; @@ -46,7 +48,9 @@ class Step extends Component { removeItem = e => { e.preventDefault(); if (window.confirm('Are you sure you want to remove this step?')) { - this.scrollToPrevious(); + if (!this.props.values.completed) { + this.setCompleted(true); + } this.props.removeItem(); } } @@ -141,7 +145,8 @@ class Step extends Component { pdf, readonly, expanded, - onToggleExpanded + onToggleExpanded, + number } = this.props; const changeFieldPrefix = values.reusableStepId ? `reusableSteps.${values.reusableStepId}.` : this.props.prefix; @@ -224,15 +229,20 @@ class Step extends Component { const repeatedFrom = getRepeatedFromProtocolIndex(values, protocol.id); const step = <> + { + values.deleted && removed + }
- + { + !values.deleted && + } { - editable && completed && !deleted && ( + editable && completed && !deleted && !values.deleted && (
{ length > 1 && ( @@ -246,7 +256,10 @@ class Step extends Component { ) }

- {`Step ${index + 1}`} + Step { !values.deleted && number + 1 } + { + {values.deleted ? ' Restore' : ''} + } {(pdf || readonly) && values.reference && (: { values.reference })} { completed && !isUndefined(values.optional) && @@ -261,7 +274,7 @@ class Step extends Component { } - {stepContent} + {!values.deleted && stepContent}

; @@ -319,14 +332,20 @@ class Step extends Component { return (
- + { + values.deleted && removed + } + { + !values.deleted && + } onToggleExpanded(index)}>

{values.reference ?

{values.reference}

:

{getStepTitle(values.title)}

} -

Step {index + 1} {values.optional === true ? '(optional)' : '(mandatory)'}{repeatedFrom ? ` - repeated from protocol ${repeatedFrom}` : ''}

+

{values.deleted ? 'Removed step' : `Step ${number + 1}`} {values.optional === true ? '(optional)' : '(mandatory)'}{repeatedFrom ? ` - repeated from protocol ${repeatedFrom}` : ''}

{stepContent}
@@ -337,7 +356,7 @@ class Step extends Component { } } -const StepSelector = ({ reusableSteps, values, onSaveSelection, length, onCancel }) => { +const StepSelector = ({reusableSteps, values, onSaveSelection, length, onCancel}) => { const DEFAULT_STEP_REFERENCE = 'Unnamed step'; const MAX_CHARACTERS_FROM_TITLE = 80; const [selectedSteps, setSelectedSteps] = useState([]); @@ -409,6 +428,7 @@ const StepsRepeater = ({ values, prefix, updateItem, editable, project, isReview singular="step" prefix={prefix} items={steps} + softDelete={true} onSave={steps => { // Extract reusable steps to save // Update reusableSteps on project only when they are complete, or have previously been saved @@ -444,8 +464,16 @@ const StepsRepeater = ({ values, prefix, updateItem, editable, project, isReview export default function Steps({project, values, ...props}) { const isReviewStep = parseInt(useParams().step, 10) === 1; - const [ steps, reusableSteps ] = hydrateSteps(project.protocols, values.steps, project.reusableSteps || {}); - + const [ allSteps, reusableSteps ] = hydrateSteps(project.protocols, values.steps, project.reusableSteps || {}); + let steps = allSteps; + if (props.pdf) { + steps = allSteps.filter(step => !step.deleted); + } else { + steps = removeNewDeleted(allSteps, props.previousProtocols.steps); + if (!props.editable && props.previousProtocols.steps.length > props.index) { + steps = addDeletedReusableSteps(steps, props.previousProtocols.steps[props.index], reusableSteps); + } + } const [expanded, setExpanded] = useState(steps.map(() => false)); const setAllExpanded = (e) => { diff --git a/package-lock.json b/package-lock.json index 8cf94a58..9ec3e386 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@asl/projects", - "version": "15.6.0", + "version": "15.6.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "@asl/projects", - "version": "15.6.0", + "version": "15.6.1", "license": "MIT", "dependencies": { "@asl/service": "^10.3.2", diff --git a/package.json b/package.json index 0cd125ab..5a3da012 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@asl/projects", - "version": "15.6.0", + "version": "15.6.1", "description": "ASL PPL prototype", "main": "client/external.js", "styles": "assets/scss/projects.scss",