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",