2022-04-21 21:12:34 +08:00
|
|
|
<template>
|
2023-03-02 18:33:40 +08:00
|
|
|
<div v-if="protocol.id" class="task-protocol">
|
2022-05-27 18:49:10 +08:00
|
|
|
<div class="task-section-header" v-if="!inRepository">
|
2022-07-05 18:49:12 +08:00
|
|
|
<div class="portocol-header-left-part">
|
2022-07-06 20:31:55 +08:00
|
|
|
<a class="task-section-caret" tabindex="0" role="button" data-toggle="collapse" href="#protocol-content" aria-expanded="true" aria-controls="protocol-content">
|
2022-07-05 18:49:12 +08:00
|
|
|
<i class="fas fa-caret-right"></i>
|
|
|
|
<div class="task-section-title">
|
|
|
|
<h2>{{ i18n.t('Protocol') }}</h2>
|
|
|
|
</div>
|
|
|
|
</a>
|
|
|
|
<div class="my-module-protocol-status">
|
|
|
|
<!-- protocol status dropdown gets mounted here -->
|
2022-04-26 20:13:17 +08:00
|
|
|
</div>
|
2022-05-04 17:49:57 +08:00
|
|
|
</div>
|
2022-07-05 18:49:12 +08:00
|
|
|
<div class="actions-block">
|
|
|
|
<div class="protocol-buttons-group">
|
2023-01-03 19:25:22 +08:00
|
|
|
<a v-if="urls.add_step_url"
|
|
|
|
class="btn btn-primary"
|
|
|
|
@keyup.enter="addStep(steps.length)"
|
|
|
|
@click="addStep(steps.length)"
|
|
|
|
tabindex="0">
|
2022-07-05 18:49:12 +08:00
|
|
|
<span class="fas fa-plus" aria-hidden="true"></span>
|
|
|
|
<span>{{ i18n.t("protocols.steps.new_step") }}</span>
|
|
|
|
</a>
|
2023-01-03 19:25:22 +08:00
|
|
|
<button class="btn btn-secondary" data-toggle="modal" data-target="#print-protocol-modal" tabindex="0">
|
2022-07-05 18:49:12 +08:00
|
|
|
<span class="fas fa-print" aria-hidden="true"></span>
|
|
|
|
<span>{{ i18n.t("protocols.print.button") }}</span>
|
2023-01-03 19:25:22 +08:00
|
|
|
</button>
|
2022-07-05 18:49:12 +08:00
|
|
|
<ProtocolOptions
|
|
|
|
v-if="protocol.attributes && protocol.attributes.urls"
|
|
|
|
:protocol="protocol"
|
|
|
|
@protocol:delete_steps="deleteSteps"
|
|
|
|
:canDeleteSteps="steps.length > 0 && urls.delete_steps_url !== null"
|
|
|
|
/>
|
|
|
|
</div>
|
2022-04-26 20:13:17 +08:00
|
|
|
</div>
|
|
|
|
</div>
|
2023-03-02 18:33:40 +08:00
|
|
|
<div id="protocol-content" class="protocol-content collapse in" aria-expanded="true">
|
2022-04-26 20:13:17 +08:00
|
|
|
<div class="protocol-description">
|
2023-01-05 18:42:59 +08:00
|
|
|
<div class="protocol-name" v-if="!inRepository">
|
2022-04-26 20:13:17 +08:00
|
|
|
<InlineEdit
|
2022-06-06 19:56:22 +08:00
|
|
|
v-if="urls.update_protocol_name_url"
|
2022-05-03 21:18:48 +08:00
|
|
|
:value="protocol.attributes.name"
|
2022-04-26 20:13:17 +08:00
|
|
|
:characterLimit="255"
|
|
|
|
:placeholder="i18n.t('my_modules.protocols.protocol_status_bar.enter_name')"
|
2022-08-10 21:39:45 +08:00
|
|
|
:allowBlank="!inRepository"
|
2022-04-26 20:13:17 +08:00
|
|
|
:attributeName="`${i18n.t('Protocol')} ${i18n.t('name')}`"
|
|
|
|
@update="updateName"
|
|
|
|
/>
|
2022-06-03 17:52:10 +08:00
|
|
|
<span v-else>
|
|
|
|
{{ protocol.attributes.name }}
|
|
|
|
</span>
|
2022-04-21 21:12:34 +08:00
|
|
|
</div>
|
2023-02-13 16:50:39 +08:00
|
|
|
<ProtocolMetadata v-if="protocol.attributes && protocol.attributes.in_repository" :protocol="protocol" @update="updateProtocol" @publish="startPublish"/>
|
2023-01-05 18:42:59 +08:00
|
|
|
<div :class="inRepository ? 'protocol-section protocol-information' : ''">
|
|
|
|
<div v-if="inRepository" id="protocol-description" class="protocol-section-header">
|
|
|
|
<div class="protocol-description-container">
|
|
|
|
<a class="protocol-section-caret" role="button" data-toggle="collapse" href="#protocol-description-container" aria-expanded="false" aria-controls="protocol-description-container">
|
|
|
|
<i class="fas fa-caret-right"></i>
|
|
|
|
<span id="protocolDescriptionLabel" class="protocol-section-title">
|
|
|
|
<h2>
|
|
|
|
{{ i18n.t("protocols.header.protocol_description") }}
|
|
|
|
</h2>
|
|
|
|
</span>
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
</div>
|
|
|
|
<div id="protocol-description-container" :class=" inRepository ? 'protocol-description collapse in' : ''" >
|
|
|
|
<div v-if="urls.update_protocol_description_url">
|
|
|
|
<Tinymce
|
|
|
|
:value="protocol.attributes.description"
|
|
|
|
:value_html="protocol.attributes.description_view"
|
|
|
|
:placeholder="i18n.t('my_modules.protocols.protocol_status_bar.empty_description_edit_label')"
|
|
|
|
:updateUrl="urls.update_protocol_description_url"
|
|
|
|
:objectType="'Protocol'"
|
|
|
|
:objectId="parseInt(protocol.id)"
|
|
|
|
:fieldName="'protocol[description]'"
|
|
|
|
:lastUpdated="protocol.attributes.updated_at"
|
2023-04-05 21:34:48 +08:00
|
|
|
:characterLimit="1000000"
|
2023-01-05 18:42:59 +08:00
|
|
|
@update="updateDescription"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
<div v-else-if="protocol.attributes.description_view" v-html="protocol.attributes.description_view"></div>
|
|
|
|
<div v-else class="empty-protocol-description">
|
|
|
|
{{ i18n.t("protocols.no_text_placeholder") }}
|
|
|
|
</div>
|
|
|
|
</div>
|
2022-06-03 17:52:10 +08:00
|
|
|
</div>
|
2022-04-21 21:12:34 +08:00
|
|
|
</div>
|
2023-01-05 18:42:59 +08:00
|
|
|
<div :class="inRepository ? 'protocol-section protocol-steps-section protocol-information' : ''">
|
|
|
|
<div v-if="inRepository" id="protocol-steps" class="protocol-section-header">
|
|
|
|
<div class="protocol-steps-container">
|
|
|
|
<a class="protocol-section-caret" role="button" data-toggle="collapse" href="#protocol-steps-container" aria-expanded="false" aria-controls="protocol-steps-container">
|
|
|
|
<i class="fas fa-caret-right"></i>
|
|
|
|
<span id="protocolStepsLabel" class="protocol-section-title">
|
|
|
|
<h2>
|
|
|
|
{{ i18n.t("protocols.header.protocol_steps") }}
|
|
|
|
</h2>
|
|
|
|
</span>
|
|
|
|
</a>
|
2022-04-26 20:13:17 +08:00
|
|
|
</div>
|
2023-01-05 18:42:59 +08:00
|
|
|
</div>
|
|
|
|
<div id="protocol-steps-container" :class=" inRepository ? 'protocol-steps collapse in' : ''">
|
|
|
|
<div v-if="steps.length > 0" class="protocol-step-actions">
|
|
|
|
<button class="btn btn-light" @click="collapseSteps" tabindex="0">
|
|
|
|
<span class="fas fa-caret-up"></span>
|
|
|
|
{{ i18n.t("protocols.steps.collapse_label") }}
|
|
|
|
</button>
|
|
|
|
<button class="btn btn-light" @click="expandSteps" tabindex="0">
|
|
|
|
<span class="fas fa-caret-down"></span>
|
|
|
|
{{ i18n.t("protocols.steps.expand_label") }}
|
|
|
|
</button>
|
|
|
|
<a v-if="urls.reorder_steps_url"
|
|
|
|
class="btn btn-light"
|
|
|
|
data-toggle="modal"
|
|
|
|
@click="startStepReorder"
|
|
|
|
@keyup.enter="startStepReorder"
|
|
|
|
:class="{'disabled': steps.length == 1}"
|
|
|
|
tabindex="0" >
|
|
|
|
<i class="fas fas-rotated-90 fa-exchange-alt" aria-hidden="true"></i>
|
|
|
|
<span>{{ i18n.t("protocols.reorder_steps.button") }}</span>
|
|
|
|
</a>
|
|
|
|
</div>
|
|
|
|
<div class="protocol-steps">
|
|
|
|
<template v-for="(step, index) in steps">
|
|
|
|
<div class="step-block" :key="step.id">
|
|
|
|
<div v-if="index > 0 && urls.add_step_url" class="insert-step" @click="addStep(index)">
|
|
|
|
<i class="fas fa-plus"></i>
|
|
|
|
</div>
|
|
|
|
<Step
|
|
|
|
:step.sync="steps[index]"
|
|
|
|
@reorder="startStepReorder"
|
|
|
|
:inRepository="inRepository"
|
|
|
|
@step:delete="updateStepsPosition"
|
|
|
|
@step:update="updateStep"
|
|
|
|
@stepUpdated="refreshProtocolStatus"
|
|
|
|
@step:insert="updateStepsPosition"
|
|
|
|
:reorderStepUrl="steps.length > 1 ? urls.reorder_steps_url : null"
|
|
|
|
/>
|
|
|
|
</div>
|
|
|
|
</template>
|
|
|
|
</div>
|
|
|
|
<button v-if="(steps.length > 0 || inRepository) && urls.add_step_url" :class="!inRepository ? 'btn btn-primary' : 'btn btn-secondary'" @click="addStep(steps.length)">
|
|
|
|
<i class="fas fa-plus"></i>
|
|
|
|
{{ i18n.t("protocols.steps.new_step") }}
|
|
|
|
</button>
|
|
|
|
</div>
|
2022-04-26 20:13:17 +08:00
|
|
|
</div>
|
2022-04-21 21:12:34 +08:00
|
|
|
</div>
|
2022-05-03 21:15:49 +08:00
|
|
|
<ProtocolModals/>
|
2022-05-30 19:45:51 +08:00
|
|
|
<ReorderableItemsModal v-if="reordering"
|
|
|
|
:title="i18n.t('protocols.reorder_steps.modal.title')"
|
|
|
|
:items="steps"
|
|
|
|
:includeNumbers="true"
|
|
|
|
@reorder="updateStepOrder"
|
|
|
|
@close="closeStepReorderModal"
|
|
|
|
/>
|
2023-02-13 16:50:39 +08:00
|
|
|
<PublishProtocol v-if="publishing"
|
|
|
|
:protocol="protocol"
|
|
|
|
@publish="publishProtocol"
|
2023-03-07 16:49:21 +08:00
|
|
|
@cancel="closePublishModal"
|
2023-02-13 16:50:39 +08:00
|
|
|
/>
|
2022-04-26 20:13:17 +08:00
|
|
|
</div>
|
2022-04-21 21:12:34 +08:00
|
|
|
</template>
|
|
|
|
|
|
|
|
<script>
|
2023-03-30 19:27:10 +08:00
|
|
|
import InlineEdit from '../shared/inline_edit.vue'
|
|
|
|
import Step from './step'
|
|
|
|
import ProtocolMetadata from './protocolMetadata'
|
|
|
|
import ProtocolOptions from './protocolOptions'
|
|
|
|
import ProtocolModals from './modals'
|
|
|
|
import Tinymce from '../shared/tinymce.vue'
|
|
|
|
import ReorderableItemsModal from './modals/reorderable_items_modal.vue'
|
2023-04-01 02:31:30 +08:00
|
|
|
import PublishProtocol from './modals/publish_protocol.vue'
|
2022-05-30 19:45:51 +08:00
|
|
|
|
2023-03-30 19:27:10 +08:00
|
|
|
import UtilsMixin from '../mixins/utils.js'
|
2022-04-22 19:07:51 +08:00
|
|
|
|
2022-04-21 21:12:34 +08:00
|
|
|
export default {
|
|
|
|
name: 'ProtocolContainer',
|
|
|
|
props: {
|
2022-04-22 19:07:51 +08:00
|
|
|
protocolUrl: {
|
|
|
|
type: String,
|
|
|
|
required: true
|
|
|
|
}
|
2022-04-21 21:12:34 +08:00
|
|
|
},
|
2023-02-13 16:50:39 +08:00
|
|
|
components: { Step, InlineEdit, ProtocolModals, ProtocolOptions, Tinymce, ReorderableItemsModal, ProtocolMetadata, PublishProtocol },
|
2022-05-30 19:45:51 +08:00
|
|
|
mixins: [UtilsMixin],
|
2022-05-27 18:49:10 +08:00
|
|
|
computed: {
|
|
|
|
inRepository() {
|
|
|
|
return this.protocol.attributes.in_repository
|
2022-06-03 17:52:10 +08:00
|
|
|
},
|
2023-03-14 20:03:37 +08:00
|
|
|
linked() {
|
|
|
|
return this.protocol.attributes.linked;
|
|
|
|
},
|
2022-06-03 17:52:10 +08:00
|
|
|
urls() {
|
|
|
|
return this.protocol.attributes.urls || {}
|
2022-05-27 18:49:10 +08:00
|
|
|
}
|
|
|
|
},
|
2022-04-21 21:12:34 +08:00
|
|
|
data() {
|
|
|
|
return {
|
2022-05-03 21:18:48 +08:00
|
|
|
protocol: {
|
|
|
|
attributes: {}
|
|
|
|
},
|
2022-07-05 18:49:12 +08:00
|
|
|
steps: [],
|
2023-02-13 16:50:39 +08:00
|
|
|
reordering: false,
|
|
|
|
publishing: false
|
2022-04-21 21:12:34 +08:00
|
|
|
}
|
|
|
|
},
|
|
|
|
created() {
|
2022-05-03 21:18:48 +08:00
|
|
|
$.get(this.protocolUrl, (result) => {
|
|
|
|
this.protocol = result.data;
|
2023-03-03 16:55:42 +08:00
|
|
|
this.$nextTick(() => {
|
|
|
|
this.refreshProtocolStatus();
|
|
|
|
});
|
2022-06-03 17:52:10 +08:00
|
|
|
$.get(this.urls.steps_url, (result) => {
|
|
|
|
this.steps = result.data
|
|
|
|
})
|
2022-04-21 21:12:34 +08:00
|
|
|
});
|
|
|
|
},
|
2022-04-22 19:07:51 +08:00
|
|
|
methods: {
|
2022-06-09 22:15:06 +08:00
|
|
|
collapseSteps() {
|
|
|
|
$('.step-container .collapse').collapse('hide')
|
|
|
|
},
|
|
|
|
expandSteps() {
|
|
|
|
$('.step-container .collapse').collapse('show')
|
|
|
|
},
|
2022-06-24 17:21:47 +08:00
|
|
|
deleteSteps() {
|
|
|
|
$.post(this.urls.delete_steps_url, () => {
|
|
|
|
this.steps = []
|
|
|
|
}).error(() => {
|
|
|
|
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger')
|
|
|
|
})
|
|
|
|
},
|
2022-05-04 17:49:57 +08:00
|
|
|
refreshProtocolStatus() {
|
2022-05-27 18:49:10 +08:00
|
|
|
if (this.inRepository) return
|
2022-05-04 17:49:57 +08:00
|
|
|
// legacy method from app/assets/javascripts/my_modules/protocols.js
|
|
|
|
refreshProtocolStatusBar();
|
2023-03-14 20:03:37 +08:00
|
|
|
|
|
|
|
// Update protocol options drowpdown for linked tasks
|
|
|
|
this.refreshProtocolDropdownOptions();
|
|
|
|
},
|
|
|
|
refreshProtocolDropdownOptions() {
|
|
|
|
if (!this.linked && this.inRepository) return
|
|
|
|
|
|
|
|
$.get(this.protocolUrl, (result) => {
|
|
|
|
this.protocol.attributes.urls = result.data.attributes.urls;
|
|
|
|
});
|
2022-05-04 17:49:57 +08:00
|
|
|
},
|
2022-06-06 19:56:22 +08:00
|
|
|
updateProtocol(attributes) {
|
|
|
|
this.protocol.attributes = attributes
|
|
|
|
},
|
2022-04-26 20:13:17 +08:00
|
|
|
updateName(newName) {
|
2022-05-03 21:18:48 +08:00
|
|
|
this.protocol.attributes.name = newName;
|
2022-04-26 20:13:17 +08:00
|
|
|
$.ajax({
|
2023-01-17 22:44:16 +08:00
|
|
|
type: 'PATCH',
|
2022-06-06 19:56:22 +08:00
|
|
|
url: this.urls.update_protocol_name_url,
|
2023-03-14 20:03:37 +08:00
|
|
|
data: { protocol: { name: newName } },
|
|
|
|
success: () => {
|
|
|
|
this.refreshProtocolStatus();
|
|
|
|
}
|
2022-04-26 20:13:17 +08:00
|
|
|
});
|
|
|
|
},
|
2022-05-11 22:08:04 +08:00
|
|
|
updateDescription(protocol) {
|
2022-12-09 19:52:18 +08:00
|
|
|
this.protocol.attributes = protocol.attributes
|
2023-03-14 20:03:37 +08:00
|
|
|
this.refreshProtocolStatus();
|
2022-05-11 22:08:04 +08:00
|
|
|
},
|
2022-04-22 19:07:51 +08:00
|
|
|
addStep(position) {
|
2022-06-03 17:52:10 +08:00
|
|
|
$.post(this.urls.add_step_url, {position: position}, (result) => {
|
2022-07-07 20:33:38 +08:00
|
|
|
result.data.newStep = true
|
2022-07-04 19:54:40 +08:00
|
|
|
this.updateStepsPosition(result.data);
|
|
|
|
|
|
|
|
// scroll to bottom if step was appended at the end
|
|
|
|
if(position === this.steps.length - 1) {
|
|
|
|
this.$nextTick(() => this.scrollToBottom());
|
|
|
|
}
|
2023-03-14 20:03:37 +08:00
|
|
|
this.refreshProtocolStatus();
|
2023-04-13 21:34:36 +08:00
|
|
|
}).error((data) => {
|
|
|
|
HelperModule.flashAlertMsg(data.responseJSON.error ? Object.values(data.responseJSON.error).join(', ') : I18n.t('errors.general'), 'danger');
|
2022-04-22 19:07:51 +08:00
|
|
|
})
|
|
|
|
},
|
|
|
|
updateStepsPosition(step, action = 'add') {
|
|
|
|
let position = step.attributes.position;
|
|
|
|
if (action === 'delete') {
|
|
|
|
this.steps.splice(position, 1)
|
|
|
|
}
|
|
|
|
let unordered_steps = this.steps.map( s => {
|
|
|
|
if (s.attributes.position >= position) {
|
|
|
|
if (action === 'add') {
|
|
|
|
s.attributes.position += 1;
|
|
|
|
} else {
|
|
|
|
s.attributes.position -= 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return s;
|
|
|
|
})
|
|
|
|
if (action === 'add') {
|
|
|
|
unordered_steps.push(step);
|
|
|
|
}
|
|
|
|
this.reorderSteps(unordered_steps)
|
|
|
|
},
|
2022-04-29 18:29:42 +08:00
|
|
|
updateStep(attributes) {
|
2022-07-07 20:33:38 +08:00
|
|
|
this.steps[attributes.position].attributes = {
|
|
|
|
...this.steps[attributes.position].attributes,
|
|
|
|
...attributes
|
|
|
|
};
|
2022-05-04 17:49:57 +08:00
|
|
|
this.refreshProtocolStatus();
|
2022-04-28 17:13:38 +08:00
|
|
|
},
|
2022-04-22 19:07:51 +08:00
|
|
|
reorderSteps(steps) {
|
|
|
|
this.steps = steps.sort((a, b) => a.attributes.position - b.attributes.position);
|
2022-05-04 17:49:57 +08:00
|
|
|
this.refreshProtocolStatus();
|
2022-05-30 19:45:51 +08:00
|
|
|
},
|
|
|
|
updateStepOrder(orderedSteps) {
|
|
|
|
orderedSteps.forEach((step, position) => {
|
|
|
|
let index = this.steps.findIndex((e) => e.id === step.id);
|
2022-06-01 18:30:09 +08:00
|
|
|
this.steps[index].attributes.position = position;
|
2022-05-30 19:45:51 +08:00
|
|
|
});
|
|
|
|
|
|
|
|
let stepPositions =
|
|
|
|
{
|
|
|
|
step_positions: this.steps.map(
|
|
|
|
(step) => [step.id, step.attributes.position]
|
|
|
|
)
|
|
|
|
};
|
|
|
|
|
|
|
|
$.ajax({
|
|
|
|
type: "POST",
|
|
|
|
url: this.protocol.attributes.urls.reorder_steps_url,
|
|
|
|
data: JSON.stringify(stepPositions),
|
|
|
|
contentType: "application/json",
|
|
|
|
dataType: "json",
|
2022-08-09 20:15:49 +08:00
|
|
|
error: (() => HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger')),
|
2023-05-04 15:48:59 +08:00
|
|
|
success: (() => this.reorderSteps(this.steps))
|
2022-05-30 19:45:51 +08:00
|
|
|
});
|
|
|
|
},
|
|
|
|
startStepReorder() {
|
|
|
|
this.reordering = true;
|
|
|
|
},
|
|
|
|
closeStepReorderModal() {
|
|
|
|
this.reordering = false;
|
2022-07-04 19:54:40 +08:00
|
|
|
},
|
2023-02-13 16:50:39 +08:00
|
|
|
startPublish() {
|
2023-04-11 18:50:14 +08:00
|
|
|
$.ajax({
|
|
|
|
type: "GET",
|
|
|
|
url: this.urls.version_comment_url,
|
|
|
|
contentType: "application/json",
|
|
|
|
dataType: "json",
|
|
|
|
success: (result) => {
|
|
|
|
this.protocol.attributes.version_comment = result.version_comment;
|
|
|
|
this.publishing = true;
|
|
|
|
}
|
|
|
|
});
|
2023-02-13 16:50:39 +08:00
|
|
|
},
|
|
|
|
closePublishModal() {
|
|
|
|
this.publishing = false;
|
|
|
|
},
|
2022-07-04 19:54:40 +08:00
|
|
|
scrollToBottom() {
|
|
|
|
window.scrollTo(0, document.body.scrollHeight);
|
2023-02-13 16:50:39 +08:00
|
|
|
},
|
|
|
|
publishProtocol(comment) {
|
|
|
|
this.protocol.attributes.version_comment = comment;
|
|
|
|
$.post(this.urls.publish_url, {version_comment: comment, view: 'show'})
|
2022-04-22 19:07:51 +08:00
|
|
|
}
|
|
|
|
}
|
2022-04-21 21:12:34 +08:00
|
|
|
}
|
|
|
|
</script>
|