Add view mode to protocol and step [SCI-6867]

This commit is contained in:
Anton 2022-06-03 11:52:10 +02:00
parent 8c78920927
commit 2da1a380c3
21 changed files with 208 additions and 121 deletions

View file

@ -32,6 +32,10 @@
border: 1px solid $brand-primary; border: 1px solid $brand-primary;
} }
.sci-checkbox-container.disabled {
pointer-events: none;
}
.sci-inline-edit { .sci-inline-edit {
font-weight: normal; font-weight: normal;

View file

@ -22,9 +22,9 @@ module StepElements
) )
end end
render json: checklist_item, serializer: ChecklistItemSerializer render json: checklist_item, serializer: ChecklistItemSerializer, user: current_user
rescue ActiveRecord::RecordInvalid rescue ActiveRecord::RecordInvalid
render json: checklist_item, serializer: ChecklistItemSerializer, status: :unprocessable_entity render json: checklist_item, serializer: ChecklistItemSerializer, user: current_user, status: :unprocessable_entity
end end
def update def update
@ -53,9 +53,9 @@ module StepElements
end end
end end
render json: @checklist_item, serializer: ChecklistItemSerializer render json: @checklist_item, serializer: ChecklistItemSerializer, user: current_user
rescue ActiveRecord::RecordInvalid rescue ActiveRecord::RecordInvalid
render json: @checklist_item, serializer: ChecklistItemSerializer, status: :unprocessable_entity render json: @checklist_item, serializer: ChecklistItemSerializer, user: current_user, status: :unprocessable_entity
end end
def destroy def destroy
@ -65,9 +65,9 @@ module StepElements
checklist_item: @checklist_item.text, checklist_item: @checklist_item.text,
checklist_name: @checklist.name checklist_name: @checklist.name
) )
render json: @checklist_item, serializer: ChecklistItemSerializer render json: @checklist_item, serializer: ChecklistItemSerializer, user: current_user
else else
render json: @checklist_item, serializer: ChecklistItemSerializer, status: :unprocessable_entity render json: @checklist_item, serializer: ChecklistItemSerializer, user: current_user, status: :unprocessable_entity
end end
end end

View file

@ -18,10 +18,7 @@ window.initProtocolComponent = () => {
}, },
data() { data() {
return { return {
protocolUrl: $('#protocolContainer').data('protocol-url'), protocolUrl: $('#protocolContainer').data('protocol-url')
stepsUrl: $('#protocolContainer').data('steps-url'),
addStepUrl: $('#protocolContainer').data('add-step-url'),
editable: $('#protocolContainer').data('editable')
}; };
} }
}); });

View file

@ -25,16 +25,18 @@
{{ i18n.t(`general.sort_new.${orderOption}`) }} {{ i18n.t(`general.sort_new.${orderOption}`) }}
</a> </a>
</li> </li>
<li role="separator" class="divider"></li> <template v-if="step.attributes.urls.update_asset_view_mode_url">
<li class="divider-label">{{ i18n.t("protocols.steps.attachments.attachments_view_mode") }}</li> <li role="separator" class="divider"></li>
<li v-for="(viewMode, index) in viewModeOptions" :key="`viewMode_${index}`"> <li class="divider-label">{{ i18n.t("protocols.steps.attachments.attachments_view_mode") }}</li>
<a <li v-for="(viewMode, index) in viewModeOptions" :key="`viewMode_${index}`">
class="attachments-view-mode action-link" <a
:class="viewMode == step.attributes.assets_view_mode ? 'selected' : ''" class="attachments-view-mode action-link"
@click="changeAttachmentsViewMode(viewMode)" :class="viewMode == step.attributes.assets_view_mode ? 'selected' : ''"
v-html="i18n.t(`protocols.steps.attachments.view_mode.${viewMode}_html`)" @click="changeAttachmentsViewMode(viewMode)"
></a> v-html="i18n.t(`protocols.steps.attachments.view_mode.${viewMode}_html`)"
</li> ></a>
</li>
</template>
</ul> </ul>
</div> </div>
</div> </div>

View file

@ -11,7 +11,7 @@
<!-- protocol status dropdown gets mounted here --> <!-- protocol status dropdown gets mounted here -->
</div> </div>
<div class="sci-btn-group actions-block"> <div class="sci-btn-group actions-block">
<a class="btn btn-primary" @click="addStep(steps.length)"> <a v-if="urls.add_step_url" class="btn btn-primary" @click="addStep(steps.length)">
<span class="fas fa-plus" aria-hidden="true"></span> <span class="fas fa-plus" aria-hidden="true"></span>
<span>{{ i18n.t("protocols.steps.new_step") }}</span> <span>{{ i18n.t("protocols.steps.new_step") }}</span>
</a> </a>
@ -26,6 +26,7 @@
<div class="protocol-description"> <div class="protocol-description">
<div class="protocol-name"> <div class="protocol-name">
<InlineEdit <InlineEdit
v-if="urls.update_protocol_url"
:value="protocol.attributes.name" :value="protocol.attributes.name"
:characterLimit="255" :characterLimit="255"
:placeholder="i18n.t('my_modules.protocols.protocol_status_bar.enter_name')" :placeholder="i18n.t('my_modules.protocols.protocol_status_bar.enter_name')"
@ -33,21 +34,27 @@
:attributeName="`${i18n.t('Protocol')} ${i18n.t('name')}`" :attributeName="`${i18n.t('Protocol')} ${i18n.t('name')}`"
@update="updateName" @update="updateName"
/> />
<span v-else>
{{ protocol.attributes.name }}
</span>
</div> </div>
<Tinymce <Tinymce
v-if="urls.update_protocol_url"
:value="protocol.attributes.description" :value="protocol.attributes.description"
:value_html="protocol.attributes.description_view" :value_html="protocol.attributes.description_view"
:placeholder="i18n.t('my_modules.protocols.protocol_status_bar.empty_description_edit_label')" :placeholder="i18n.t('my_modules.protocols.protocol_status_bar.empty_description_edit_label')"
:updateUrl="protocolUrl" :updateUrl="urls.update_protocol"
:objectType="'Protocol'" :objectType="'Protocol'"
:objectId="parseInt(protocol.id)" :objectId="parseInt(protocol.id)"
:fieldName="'protocol[description]'" :fieldName="'protocol[description]'"
:lastUpdated="protocol.attributes.updated_at" :lastUpdated="protocol.attributes.updated_at"
@update="updateDescription" @update="updateDescription"
/> />
<div v-else v-html="protocol.attributes.description_view">
</div>
</div> </div>
<div class="protocol-step-actions"> <div class="protocol-step-actions">
<a class="btn btn-default" data-toggle="modal" @click="startStepReorder"> <a v-if="urls.reorder_steps_url" class="btn btn-default" data-toggle="modal" @click="startStepReorder">
<span class="fas fa-arrows-alt-v" aria-hidden="true"></span> <span class="fas fa-arrows-alt-v" aria-hidden="true"></span>
<span>{{ i18n.t("protocols.reorder_steps.button") }}</span> <span>{{ i18n.t("protocols.reorder_steps.button") }}</span>
</a> </a>
@ -55,7 +62,7 @@
<div class="protocol-steps"> <div class="protocol-steps">
<template v-for="(step, index) in steps"> <template v-for="(step, index) in steps">
<div class="step-block" :key="step.id"> <div class="step-block" :key="step.id">
<div v-if="index > 0" class="insert-step" @click="addStep(index)"> <div v-if="index > 0 && urls.add_step_url" class="insert-step" @click="addStep(index)">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
</div> </div>
<Step <Step
@ -64,11 +71,12 @@
:inRepository="inRepository" :inRepository="inRepository"
@step:delete="updateStepsPosition" @step:delete="updateStepsPosition"
@step:update="updateStep" @step:update="updateStep"
:reorderStepUrl="urls.reorder_steps_url"
/> />
</div> </div>
</template> </template>
</div> </div>
<button class="btn btn-primary" @click="addStep(steps.length)"> <button v-if="urls.add_step_url" class="btn btn-primary" @click="addStep(steps.length)">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
{{ i18n.t("protocols.steps.new_step") }} {{ i18n.t("protocols.steps.new_step") }}
</button> </button>
@ -100,18 +108,6 @@
protocolUrl: { protocolUrl: {
type: String, type: String,
required: true required: true
},
stepsUrl: {
type: String,
required: true
},
addStepUrl: {
type: String,
required: true
},
editable:{
Boolean,
required: true
} }
}, },
components: { Step, InlineEdit, ProtocolModals, ProtocolOptions, Tinymce, ReorderableItemsModal }, components: { Step, InlineEdit, ProtocolModals, ProtocolOptions, Tinymce, ReorderableItemsModal },
@ -119,6 +115,9 @@
computed: { computed: {
inRepository() { inRepository() {
return this.protocol.attributes.in_repository return this.protocol.attributes.in_repository
},
urls() {
return this.protocol.attributes.urls || {}
} }
}, },
data() { data() {
@ -133,10 +132,10 @@
created() { created() {
$.get(this.protocolUrl, (result) => { $.get(this.protocolUrl, (result) => {
this.protocol = result.data; this.protocol = result.data;
$.get(this.urls.steps_url, (result) => {
this.steps = result.data
})
}); });
$.get(this.stepsUrl, (result) => {
this.steps = result.data
})
}, },
methods: { methods: {
refreshProtocolStatus() { refreshProtocolStatus() {
@ -149,7 +148,7 @@
this.refreshProtocolStatus(); this.refreshProtocolStatus();
$.ajax({ $.ajax({
type: 'PATCH', type: 'PATCH',
url: this.protocolUrl, url: this.urls.update_protocol_url,
data: { protocol: { name: newName } } data: { protocol: { name: newName } }
}); });
}, },
@ -157,7 +156,7 @@
this.protocol.attributes = protocol.data.attributes this.protocol.attributes = protocol.data.attributes
}, },
addStep(position) { addStep(position) {
$.post(this.addStepUrl, {position: position}, (result) => { $.post(this.urls.add_step_url, {position: position}, (result) => {
this.updateStepsPosition(result.data) this.updateStepsPosition(result.data)
}) })
this.refreshProtocolStatus(); this.refreshProtocolStatus();

View file

@ -12,7 +12,7 @@
<StorageUsage v-if="step.attributes.storage_limit" :step="step"/> <StorageUsage v-if="step.attributes.storage_limit" :step="step"/>
</div> </div>
<div class="step-header step-element-header"> <div class="step-header step-element-header">
<div class="step-element-grip" @click="$emit('reorder')"> <div v-if="reorderStepUrl" class="step-element-grip" @click="$emit('reorder')">
<i class="fas fa-grip-vertical"></i> <i class="fas fa-grip-vertical"></i>
</div> </div>
<a class="step-collapse-link" <a class="step-collapse-link"
@ -29,15 +29,19 @@
</div> </div>
<div class="step-name-container"> <div class="step-name-container">
<InlineEdit <InlineEdit
v-if="urls.update_url"
:value="step.attributes.name" :value="step.attributes.name"
:characterLimit="255" :characterLimit="255"
:allowBlank="false" :allowBlank="false"
:attributeName="`${i18n.t('Step')} ${i18n.t('name')}`" :attributeName="`${i18n.t('Step')} ${i18n.t('name')}`"
@update="updateName" @update="updateName"
/> />
<span v-else>
{{ step.attributes.name }}
</span>
</div> </div>
<div class="step-actions-container"> <div class="step-actions-container">
<div class="dropdown"> <div v-if="urls.update_url" class="dropdown">
<button class="btn btn-light dropdown-toggle insert-button" type="button" :id="'stepInserMenu_' + step.id" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> <button class="btn btn-light dropdown-toggle insert-button" type="button" :id="'stepInserMenu_' + step.id" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
{{ i18n.t('protocols.steps.insert.button') }} {{ i18n.t('protocols.steps.insert.button') }}
<span class="caret"></span> <span class="caret"></span>
@ -79,7 +83,7 @@
{{ step.attributes.comments_count }} {{ step.attributes.comments_count }}
</span> </span>
</a> </a>
<div class="step-actions-container"> <div v-if="urls.update_url" class="step-actions-container">
<div class="dropdown"> <div class="dropdown">
<button class="btn btn-light dropdown-toggle insert-button" type="button" :id="'stepInserMenu_' + step.id" data-toggle="dropdown" data-display="static" aria-haspopup="true" aria-expanded="true"> <button class="btn btn-light dropdown-toggle insert-button" type="button" :id="'stepInserMenu_' + step.id" data-toggle="dropdown" data-display="static" aria-haspopup="true" aria-expanded="true">
<i class="fas fa-ellipsis-v"></i> <i class="fas fa-ellipsis-v"></i>
@ -88,11 +92,11 @@
<li class="title"> <li class="title">
{{ i18n.t('protocols.steps.options_dropdown.title') }} {{ i18n.t('protocols.steps.options_dropdown.title') }}
</li> </li>
<li class="action" @click="openReorderModal"> <li v-if="urls.reorder_elements_url" class="action" @click="openReorderModal">
<i class="fas fa-arrows-alt-v"></i> <i class="fas fa-arrows-alt-v"></i>
{{ i18n.t('protocols.steps.options_dropdown.rearrange') }} {{ i18n.t('protocols.steps.options_dropdown.rearrange') }}
</li> </li>
<li class="action" @click="showDeleteModal"> <li v-if="urls.delete_url" class="action" @click="showDeleteModal">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
{{ i18n.t('protocols.steps.options_dropdown.delete') }} {{ i18n.t('protocols.steps.options_dropdown.delete') }}
</li> </li>
@ -107,6 +111,7 @@
:is="elements[index].attributes.orderable_type" :is="elements[index].attributes.orderable_type"
:key="index" :key="index"
:element.sync="elements[index]" :element.sync="elements[index]"
:reorderElementUrl="urls.reorder_elements_url"
@component:delete="deleteElement" @component:delete="deleteElement"
@update="updateElement" @update="updateElement"
@reorder="openReorderModal" @reorder="openReorderModal"
@ -166,6 +171,10 @@
type: Boolean, type: Boolean,
required: true required: true
}, },
reorderStepUrl: {
type: String,
required: true
}
}, },
data() { data() {
return { return {
@ -200,16 +209,19 @@
computed: { computed: {
reorderableElements() { reorderableElements() {
return this.elements.map((e) => { return { id: e.id, attributes: e.attributes.orderable } }) return this.elements.map((e) => { return { id: e.id, attributes: e.attributes.orderable } })
},
urls() {
return this.step.attributes.urls || {}
} }
}, },
methods: { methods: {
loadAttachments() { loadAttachments() {
$.get(this.step.attributes.urls.attachments_url, (result) => { $.get(this.urls.attachments_url, (result) => {
this.attachments = result.data this.attachments = result.data
}); });
}, },
loadElements() { loadElements() {
$.get(this.step.attributes.urls.elements_url, (result) => { $.get(this.urls.elements_url, (result) => {
this.elements = result.data this.elements = result.data
}); });
}, },
@ -221,7 +233,7 @@
}, },
deleteStep() { deleteStep() {
$.ajax({ $.ajax({
url: this.step.attributes.urls.delete_url, url: this.urls.delete_url,
type: 'DELETE', type: 'DELETE',
success: (result) => { success: (result) => {
this.$emit( this.$emit(
@ -233,9 +245,11 @@
}); });
}, },
changeState() { changeState() {
if (!this.urls.state_url) return;
this.step.attributes.completed = !this.step.attributes.completed; this.step.attributes.completed = !this.step.attributes.completed;
this.$emit('step:update', this.step.attributes) this.$emit('step:update', this.step.attributes)
$.post(this.step.attributes.urls.state_url, {completed: this.step.attributes.completed}).error(() => { $.post(this.urls.state_url, {completed: this.step.attributes.completed}).error(() => {
this.step.attributes.completed = !this.step.attributes.completed; this.step.attributes.completed = !this.step.attributes.completed;
this.$emit('step:update', this.step.attributes) this.$emit('step:update', this.step.attributes)
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger'); HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
@ -288,7 +302,7 @@
$.ajax({ $.ajax({
type: "POST", type: "POST",
url: this.step.attributes.urls.reorder_elements_url, url: this.urls.reorder_elements_url,
data: JSON.stringify(elementPositions), data: JSON.stringify(elementPositions),
contentType: "application/json", contentType: "application/json",
dataType: "json", dataType: "json",
@ -299,7 +313,7 @@
}, },
updateName(newName) { updateName(newName) {
$.ajax({ $.ajax({
url: this.step.attributes.urls.update_url, url: this.urls.update_url,
type: 'PATCH', type: 'PATCH',
data: {step: {name: newName}}, data: {step: {name: newName}},
success: (result) => { success: (result) => {
@ -308,7 +322,7 @@
}); });
}, },
createElement(elementType) { createElement(elementType) {
$.post(this.step.attributes.urls[`create_${elementType}_url`], (result) => { $.post(this.urls[`create_${elementType}_url`], (result) => {
this.elements.push(result.data) this.elements.push(result.data)
}).error(() => { }).error(() => {
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger'); HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');

View file

@ -8,7 +8,7 @@
aria-labelledby="dropdownAssetContextMenu" aria-labelledby="dropdownAssetContextMenu"
:data-asset-id="attachment.id" :data-asset-id="attachment.id"
> >
<li v-if="attachment.attributes.wopi" > <li v-if="attachment.attributes.wopi && attachment.attributes.urls.edit_asset" >
<a :href="attachment.attributes.urls.edit_asset" <a :href="attachment.attributes.urls.edit_asset"
id="wopi_file_edit_button" id="wopi_file_edit_button"
class="btn btn-light" class="btn btn-light"
@ -20,7 +20,7 @@
{{ attachment.attributes.wopi_context.button_text }} {{ attachment.attributes.wopi_context.button_text }}
</a> </a>
</li> </li>
<li v-if="attachment.attributes.asset_type == 'marvinjs'"> <li v-if="attachment.attributes.asset_type == 'marvinjs' && attachment.attributes.urls.marvin_js_start_edit">
<a class="btn btn-light marvinjs-edit-button" <a class="btn btn-light marvinjs-edit-button"
:data-sketch-id="attachment.id" :data-sketch-id="attachment.id"
:data-update-url="attachment.attributes.urls.marvin_js" :data-update-url="attachment.attributes.urls.marvin_js"
@ -32,7 +32,7 @@
{{ i18n.t('assets.file_preview.edit_in_marvinjs') }} {{ i18n.t('assets.file_preview.edit_in_marvinjs') }}
</a> </a>
</li> </li>
<li v-if="attachment.attributes.image_editable"> <li v-if="attachment.attributes.image_editable && attachment.attributes.urls.start_edit_image">
<a class="btn btn-light image-edit-button" <a class="btn btn-light image-edit-button"
:data-image-id="attachment.id" :data-image-id="attachment.id"
:data-image-name="attachment.attributes.file_name" :data-image-name="attachment.attributes.file_name"
@ -51,25 +51,29 @@
{{ i18n.t('Download') }} {{ i18n.t('Download') }}
</a> </a>
</li> </li>
<li role="separator" class="divider"></li> <template v-if="attachment.attributes.urls.toggle_view_mode">
<li class="divider-label"> <li role="separator" class="divider"></li>
{{ i18n.t("assets.context_menu.set_view_size") }} <li class="divider-label">
</li> {{ i18n.t("assets.context_menu.set_view_size") }}
<li v-for="(viewMode, index) in viewModeOptions" :key="`viewMode_${index}`"> </li>
<a <li v-for="(viewMode, index) in viewModeOptions" :key="`viewMode_${index}`">
class="change-preview-type" <a
:class="viewMode == attachment.attributes.view_mode ? 'selected' : ''" class="change-preview-type"
@click.prevent.stop="changeViewMode(viewMode)" :class="viewMode == attachment.attributes.view_mode ? 'selected' : ''"
v-html="i18n.t(`assets.context_menu.${viewMode}_html`)" @click.prevent.stop="changeViewMode(viewMode)"
></a> v-html="i18n.t(`assets.context_menu.${viewMode}_html`)"
</li> ></a>
<li role="separator" class="divider"></li> </li>
<li> </template>
<a @click.prevent.stop="deleteModal = true"> <template v-if="attachment.attributes.urls.delete">
<i class="fas fa-trash"></i> <li role="separator" class="divider"></li>
{{ i18n.t("assets.context_menu.delete") }} <li>
</a> <a @click.prevent.stop="deleteModal = true">
</li> <i class="fas fa-trash"></i>
{{ i18n.t("assets.context_menu.delete") }}
</a>
</li>
</template>
</ul> </ul>
<deleteAttachmentModal <deleteAttachmentModal
v-if="deleteModal" v-if="deleteModal"

View file

@ -1,11 +1,12 @@
<template> <template>
<div class="step-checklist-container"> <div class="step-checklist-container">
<div class="step-element-header" :class="{ 'locked': locked, 'editing-name': editingName }"> <div class="step-element-header" :class="{ 'locked': locked, 'editing-name': editingName }">
<div class="step-element-grip" @click="$emit('reorder')"> <div v-if="reorderElementUrl" class="step-element-grip" @click="$emit('reorder')">
<i class="fas fa-grip-vertical"></i> <i class="fas fa-grip-vertical"></i>
</div> </div>
<div class="step-element-name"> <div class="step-element-name">
<InlineEdit <InlineEdit
v-if="element.attributes.orderable.urls.update_url"
:value="element.attributes.orderable.name" :value="element.attributes.orderable.name"
:characterLimit="255" :characterLimit="255"
:placeholder="''" :placeholder="''"
@ -16,12 +17,15 @@
@editingDisabled="editingName = false" @editingDisabled="editingName = false"
@update="updateName" @update="updateName"
/> />
<span v-else>
{{ element.attributes.orderable.name }}
</span>
</div> </div>
<div class="step-element-controls"> <div class="step-element-controls">
<button class="btn icon-btn btn-light" @click="editingName = true"> <button v-if="element.attributes.orderable.urls.update_url" class="btn icon-btn btn-light" @click="editingName = true">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</button> </button>
<button class="btn icon-btn btn-light" @click="showDeleteModal"> <button v-if="element.attributes.orderable.urls.delete_url" class="btn icon-btn btn-light" @click="showDeleteModal">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</div> </div>
@ -33,7 +37,7 @@
:dragClass="'step-checklist-item-drag'" :dragClass="'step-checklist-item-drag'"
:chosenClass="'step-checklist-item-chosen'" :chosenClass="'step-checklist-item-chosen'"
:handle="'.step-element-grip'" :handle="'.step-element-grip'"
:disabled="editingItem" :disabled="editingItem && !element.attributes.orderable.urls.reorder_url"
@start="startReorder" @start="startReorder"
@end="endReorder" @end="endReorder"
> >
@ -42,6 +46,7 @@
:key="checklistItem.attributes.id" :key="checklistItem.attributes.id"
:checklistItem="checklistItem" :checklistItem="checklistItem"
:locked="locked" :locked="locked"
:reorderChecklistItemUrl="element.attributes.orderable.urls.reorder_url"
@editStart="editingItem = true" @editStart="editingItem = true"
@editEnd="editingItem = false" @editEnd="editingItem = false"
@update="saveItem" @update="saveItem"
@ -50,7 +55,7 @@
@multilinePaste="handleMultilinePaste" @multilinePaste="handleMultilinePaste"
/> />
</Draggable> </Draggable>
<div class="btn btn-light step-checklist-add-item" @click="addItem"> <div v-if="element.attributes.orderable.urls.create_item_url" class="btn btn-light step-checklist-add-item" @click="addItem">
<i class="fas fa-plus"></i> <i class="fas fa-plus"></i>
{{ i18n.t('protocols.steps.insert.checklist_item') }} {{ i18n.t('protocols.steps.insert.checklist_item') }}
</div> </div>
@ -74,6 +79,9 @@
element: { element: {
type: Object, type: Object,
required: true required: true
},
reorderElementUrl: {
type: String
} }
}, },
data() { data() {

View file

@ -1,17 +1,21 @@
<template> <template>
<div class="step-checklist-item"> <div class="step-checklist-item">
<div class="step-element-header" :class="{ 'locked': locked || editingText, 'editing-name': editingText }"> <div class="step-element-header" :class="{ 'locked': locked || editingText, 'editing-name': editingText }">
<div class="step-element-grip"> <div v-if="reorderChecklistItemUrl" class="step-element-grip">
<i class="fas fa-grip-vertical"></i> <i class="fas fa-grip-vertical"></i>
</div> </div>
<div class="step-element-name" :class="{ 'done': checklistItem.attributes.checked }"> <div class="step-element-name" :class="{ 'done': checklistItem.attributes.checked }">
<div class="sci-checkbox-container"> <div class="sci-checkbox-container" :class="{ 'disabled': !checklistItem.attributes.urls.update_url}">
<input ref="checkbox" type="checkbox" class="sci-checkbox" :checked="checklistItem.attributes.checked" @change="toggleChecked" /> <input ref="checkbox"
<span class="sci-checkbox-label"> type="checkbox"
class="sci-checkbox"
:checked="checklistItem.attributes.checked" @change="toggleChecked($event)" />
<span class="sci-checkbox-label" >
</span> </span>
</div> </div>
<div class="step-checklist-text"> <div class="step-checklist-text">
<InlineEdit <InlineEdit
v-if="checklistItem.attributes.urls.update_url"
:value="checklistItem.attributes.text" :value="checklistItem.attributes.text"
:characterLimit="10000" :characterLimit="10000"
:placeholder="''" :placeholder="''"
@ -25,13 +29,16 @@
@delete="checklistItem.attributes.id ? deleteElement() : removeItem()" @delete="checklistItem.attributes.id ? deleteElement() : removeItem()"
@multilinePaste="(data) => { $emit('multilinePaste', data) && removeItem() }" @multilinePaste="(data) => { $emit('multilinePaste', data) && removeItem() }"
/> />
<span v-else>
{{ checklistItem.attributes.text }}
</span>
</div> </div>
</div> </div>
<div class="step-element-controls"> <div class="step-element-controls">
<button class="btn icon-btn btn-light" @click="enableTextEdit"> <button v-if="checklistItem.attributes.urls.update_url" class="btn icon-btn btn-light" @click="enableTextEdit">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</button> </button>
<button class="btn icon-btn btn-light" @click="showDeleteModal"> <button v-if="checklistItem.attributes.urls.delete_url" class="btn icon-btn btn-light" @click="showDeleteModal">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</div> </div>
@ -57,6 +64,9 @@
locked: { locked: {
type: Boolean, type: Boolean,
default: false default: false
},
reorderChecklistItemUrl: {
type: String
} }
}, },
data() { data() {
@ -83,7 +93,8 @@
this.editingText = false; this.editingText = false;
this.$emit('editEnd'); this.$emit('editEnd');
}, },
toggleChecked() { toggleChecked(e) {
if (!this.checklistItem.attributes.urls.update_url) return
this.checklistItem.attributes.checked = this.$refs.checkbox.checked; this.checklistItem.attributes.checked = this.$refs.checkbox.checked;
this.update(); this.update();
}, },

View file

@ -1,11 +1,12 @@
<template> <template>
<div class="step-table-container"> <div class="step-table-container">
<div class="step-element-header" :class="{ 'editing-name': editingName }"> <div class="step-element-header" :class="{ 'editing-name': editingName }">
<div class="step-element-grip" @click="$emit('reorder')"> <div v-if="reorderElementUrl" class="step-element-grip" @click="$emit('reorder')">
<i class="fas fa-grip-vertical"></i> <i class="fas fa-grip-vertical"></i>
</div> </div>
<div class="step-element-name"> <div class="step-element-name">
<InlineEdit <InlineEdit
v-if="element.attributes.orderable.urls.update_url"
:value="element.attributes.orderable.name" :value="element.attributes.orderable.name"
:characterLimit="255" :characterLimit="255"
:placeholder="''" :placeholder="''"
@ -16,18 +17,21 @@
@editingDisabled="disableNameEdit" @editingDisabled="disableNameEdit"
@update="updateName" @update="updateName"
/> />
<span v-else>
{{ element.attributes.orderable.name }}
</span>
</div> </div>
<div class="step-element-controls"> <div class="step-element-controls">
<button class="btn icon-btn btn-light" @click="enableNameEdit"> <button v-if="element.attributes.orderable.urls.update_url" class="btn icon-btn btn-light" @click="enableNameEdit">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</button> </button>
<button class="btn icon-btn btn-light" @click="showDeleteModal"> <button v-if="element.attributes.orderable.urls.delete_url" class="btn icon-btn btn-light" @click="showDeleteModal">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</div> </div>
</div> </div>
<div :class="'step-table ' + (editingTable ? 'edit' : 'view')"> <div :class="'step-table ' + (editingTable ? 'edit' : 'view')">
<div class="enable-edit-mode" v-if="!editingTable" @click="enableTableEdit"> <div class="enable-edit-mode" v-if="!editingTable && element.attributes.orderable.urls.update_url" @click="enableTableEdit">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</div> </div>
<div ref="hotTable" class="hot-table-container"> <div ref="hotTable" class="hot-table-container">
@ -61,6 +65,9 @@
element: { element: {
type: Object, type: Object,
required: true required: true
},
reorderElementUrl: {
type: String
} }
}, },
data() { data() {

View file

@ -1,19 +1,20 @@
<template> <template>
<div class="step-text-container" :class="{ 'edit': inEditMode }"> <div class="step-text-container" :class="{ 'edit': inEditMode }">
<div class="action-container" @click="enableEditMode"> <div class="action-container" @click="enableEditMode">
<div class="element-grip" @click="$emit('reorder')"> <div v-if="reorderElementUrl" class="element-grip" @click="$emit('reorder')">
<i class="fas fa-grip-vertical"></i> <i class="fas fa-grip-vertical"></i>
</div> </div>
<div class="buttons-container"> <div class="buttons-container">
<button class="btn icon-btn btn-light"> <button v-if="element.attributes.orderable.urls.update_url" class="btn icon-btn btn-light">
<i class="fas fa-pen"></i> <i class="fas fa-pen"></i>
</button> </button>
<button class="btn icon-btn btn-light" @click="showDeleteModal"> <button v-if="element.attributes.orderable.urls.delete_url" class="btn icon-btn btn-light" @click="showDeleteModal">
<i class="fas fa-trash"></i> <i class="fas fa-trash"></i>
</button> </button>
</div> </div>
</div> </div>
<Tinymce <Tinymce
v-if="element.attributes.orderable.urls.update_url"
:inEditMode="inEditMode" :inEditMode="inEditMode"
:value="element.attributes.orderable.text" :value="element.attributes.orderable.text"
:value_html="element.attributes.orderable.text_view" :value_html="element.attributes.orderable.text_view"
@ -26,6 +27,7 @@
@update="update" @update="update"
@editingDisabled="disableEditMode" @editingDisabled="disableEditMode"
/> />
<div v-else v-html="element.attributes.orderable.text_view"></div>
<deleteElementModal v-if="confirmingDelete" @confirm="deleteElement($event)" @cancel="closeDeleteModal"/> <deleteElementModal v-if="confirmingDelete" @confirm="deleteElement($event)" @cancel="closeDeleteModal"/>
</div> </div>
</template> </template>
@ -43,6 +45,9 @@
element: { element: {
type: Object, type: Object,
required: true required: true
},
reorderElementUrl: {
type: String
} }
}, },
data() { data() {
@ -52,6 +57,7 @@
}, },
methods: { methods: {
enableEditMode() { enableEditMode() {
if (!this.element.attributes.orderable.urls.update_url) return
this.inEditMode = true this.inEditMode = true
}, },
disableEditMode() { disableEditMode() {

View file

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class AssetSerializer < ActiveModel::Serializer class AssetSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
include FileIconsHelper include FileIconsHelper
include ActionView::Helpers::NumberHelper include ActionView::Helpers::NumberHelper
@ -97,15 +98,17 @@ class AssetSerializer < ActiveModel::Serializer
download: rails_blob_path(object.file, disposition: 'attachment'), download: rails_blob_path(object.file, disposition: 'attachment'),
load_asset: load_asset_path(object), load_asset: load_asset_path(object),
asset_file: asset_file_url_path(object), asset_file: asset_file_url_path(object),
marvin_js: marvin_js_asset_path(object),
marvin_js_icon: image_path('icon_small/marvinjs.svg'),
}
urls.merge!(
toggle_view_mode: toggle_view_mode_path(object), toggle_view_mode: toggle_view_mode_path(object),
edit_asset: edit_asset_path(object), edit_asset: edit_asset_path(object),
marvin_js: marvin_js_asset_path(object),
marvin_js_start_edit: start_editing_marvin_js_asset_path(object), marvin_js_start_edit: start_editing_marvin_js_asset_path(object),
marvin_js_icon: image_path('icon_small/marvinjs.svg'),
start_edit_image: start_edit_image_path(object), start_edit_image: start_edit_image_path(object),
delete: asset_destroy_path(object) delete: asset_destroy_path(object)
} ) if can_manage_asset?(object)
urls[:wopi_action] = object.get_action_url(@instance_options[:user], 'embedview') if wopi urls[:wopi_action] = object.get_action_url(@instance_options[:user], 'embedview') if wopi && can_manage_asset?(@asset)
urls[:blob] = rails_blob_path(object.file, disposition: 'attachment') if object.file.attached? urls[:blob] = rails_blob_path(object.file, disposition: 'attachment') if object.file.attached?
urls urls

View file

@ -1,12 +1,15 @@
# frozen_string_literal: true # frozen_string_literal: true
class ChecklistItemSerializer < ActiveModel::Serializer class ChecklistItemSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
attributes :id, :text, :checked, :position, :urls attributes :id, :text, :checked, :position, :urls
def urls def urls
return {} if object.destroyed? || !object.persisted? return {} if object.destroyed? ||
!object.persisted? ||
!can_manage_step?(scope[:user] || @instance_options[:user], object.checklist.step)
{ {
update_url: step_checklist_checklist_item_path(object.checklist.step, object.checklist, object), update_url: step_checklist_checklist_item_path(object.checklist.step, object.checklist, object),

View file

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class ChecklistSerializer < ActiveModel::Serializer class ChecklistSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
attributes :id, :name, :urls, :icon attributes :id, :name, :urls, :icon
@ -11,7 +12,7 @@ class ChecklistSerializer < ActiveModel::Serializer
end end
def urls def urls
return {} if object.destroyed? return {} if object.destroyed? || !can_manage_step?(scope[:user], object.step)
{ {
delete_url: step_checklist_path(object.step, object), delete_url: step_checklist_path(object.step, object),

View file

@ -30,7 +30,10 @@ class ProtocolSerializer < ActiveModel::Serializer
save_to_repo_url: save_to_repo_url, save_to_repo_url: save_to_repo_url,
export_url: export_url, export_url: export_url,
import_url: import_url, import_url: import_url,
reorder_steps_url: reorder_protocol_steps_url(protocol_id: object.id) steps_url: steps_url,
reorder_steps_url: reorder_steps_url,
add_step_url: add_step_url,
update_protocol_url: update_protocol_url
} }
end end
@ -63,4 +66,30 @@ class ProtocolSerializer < ActiveModel::Serializer
export_protocols_path(protocol_ids: object.id, my_module_id: object.my_module.id) export_protocols_path(protocol_ids: object.id, my_module_id: object.my_module.id)
end end
def steps_url
return unless can_read_protocol_in_module?(object) || can_read_protocol_in_repository?(object)
steps_path(protocol_id: object.id)
end
def reorder_steps_url
return unless can_manage_protocol_in_module?(object) || can_manage_protocol_in_repository?(object)
reorder_protocol_steps_url(protocol_id: object.id)
end
def add_step_url
return unless (in_repository ? can_manage_protocol_in_repository?(object) : can_manage_my_module_steps?(object.my_module))
protocol_steps_path(protocol_id: object.id)
end
def update_protocol_url
if in_repository
protocol_path(@protocol) if can_manage_protocol_in_repository?(object)
else
protocol_my_module_path(object.my_module) if can_manage_protocol_in_module?(object)
end
end
end end

View file

@ -6,9 +6,9 @@ class StepOrderableElementSerializer < ActiveModel::Serializer
def orderable def orderable
case object.orderable_type case object.orderable_type
when 'Checklist' when 'Checklist'
ChecklistSerializer.new(object.orderable).as_json ChecklistSerializer.new(object.orderable, scope: { user: @instance_options[:user] }).as_json
when 'StepTable' when 'StepTable'
TableSerializer.new(object.orderable.table).as_json TableSerializer.new(object.orderable.table, scope: { user: @instance_options[:user] }).as_json
when 'StepText' when 'StepText'
StepTextSerializer.new(object.orderable, scope: { user: @instance_options[:user] }).as_json StepTextSerializer.new(object.orderable, scope: { user: @instance_options[:user] }).as_json
end end

View file

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class StepSerializer < ActiveModel::Serializer class StepSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
include ApplicationHelper include ApplicationHelper
include CommentHelper include CommentHelper
@ -52,12 +53,16 @@ class StepSerializer < ActiveModel::Serializer
end end
def urls def urls
{
urls_list = {
elements_url: elements_step_path(object),
attachments_url: attachments_step_path(object)
}
urls_list.merge!({
delete_url: step_path(object), delete_url: step_path(object),
state_url: toggle_step_state_step_path(object), state_url: toggle_step_state_step_path(object),
update_url: step_path(object), update_url: step_path(object),
elements_url: elements_step_path(object),
attachments_url: attachments_step_path(object),
create_table_url: step_tables_path(object), create_table_url: step_tables_path(object),
create_text_url: step_texts_path(object), create_text_url: step_texts_path(object),
create_checklist_url: step_checklists_path(object), create_checklist_url: step_checklists_path(object),
@ -66,6 +71,8 @@ class StepSerializer < ActiveModel::Serializer
direct_upload_url: rails_direct_uploads_url, direct_upload_url: rails_direct_uploads_url,
upload_attachment_url: upload_attachment_step_path(object), upload_attachment_url: upload_attachment_step_path(object),
reorder_elements_url: reorder_step_step_orderable_elements_path(step_id: object.id) reorder_elements_url: reorder_step_step_orderable_elements_path(step_id: object.id)
} }) if can_manage_step?(object)
urls_list
end end
end end

View file

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class StepTextSerializer < ActiveModel::Serializer class StepTextSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
include ApplicationHelper include ApplicationHelper
include ActionView::Helpers::TextHelper include ActionView::Helpers::TextHelper
@ -28,7 +29,7 @@ class StepTextSerializer < ActiveModel::Serializer
end end
def urls def urls
return if object.destroyed? return {} if object.destroyed? || !can_manage_step?(scope[:user], object.step)
{ {
delete_url: step_text_path(object.step, object), delete_url: step_text_path(object.step, object),

View file

@ -1,6 +1,7 @@
# frozen_string_literal: true # frozen_string_literal: true
class TableSerializer < ActiveModel::Serializer class TableSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
attributes :name, :contents, :urls, :icon attributes :name, :contents, :urls, :icon
@ -18,6 +19,8 @@ class TableSerializer < ActiveModel::Serializer
object.reload unless object.step object.reload unless object.step
return {} unless can_manage_step?(scope[:user], object.step)
{ {
delete_url: step_table_path(object.step, object), delete_url: step_table_path(object.step, object),
update_url: step_table_path(object.step, object) update_url: step_table_path(object.step, object)

View file

@ -124,17 +124,11 @@
<div <div
id="protocolContainer" id="protocolContainer"
data-protocol-url="<%= protocol_my_module_path(@my_module) %>" data-protocol-url="<%= protocol_my_module_path(@my_module) %>"
data-add-step-url="<%= protocol_steps_path(protocol_id: @protocol.id) %>"
data-steps-url="<%= steps_path(protocol_id: @protocol) %>"
data-editable="<%= can_manage_protocol_in_module?(@protocol) %>"
data-date-format="<%= datetime_picker_format_date_only %>" data-date-format="<%= datetime_picker_format_date_only %>"
data-user-utc-offset="<%= ActiveSupport::TimeZone.find_tzinfo(current_user.time_zone).utc_offset %>" data-user-utc-offset="<%= ActiveSupport::TimeZone.find_tzinfo(current_user.time_zone).utc_offset %>"
> >
<protocol-container <protocol-container
:protocol-url="protocolUrl" :protocol-url="protocolUrl"
:steps-url="stepsUrl"
:add-step-url="addStepUrl"
:editable="editable"
/> />
</div> </div>
</div> </div>

View file

@ -3,17 +3,11 @@
<div <div
id="protocolContainer" id="protocolContainer"
data-protocol-url="<%= protocol_path(@protocol) %>" data-protocol-url="<%= protocol_path(@protocol) %>"
data-add-step-url="<%= protocol_steps_path(protocol_id: @protocol.id) %>"
data-steps-url="<%= steps_path(protocol_id: @protocol) %>"
data-editable="<%= can_manage_protocol_in_module?(@protocol) %>"
data-date-format="<%= datetime_picker_format_date_only %>" data-date-format="<%= datetime_picker_format_date_only %>"
data-user-utc-offset="<%= ActiveSupport::TimeZone.find_tzinfo(current_user.time_zone).utc_offset %>" data-user-utc-offset="<%= ActiveSupport::TimeZone.find_tzinfo(current_user.time_zone).utc_offset %>"
> >
<protocol-container <protocol-container
:protocol-url="protocolUrl" :protocol-url="protocolUrl"
:steps-url="stepsUrl"
:add-step-url="addStepUrl"
:editable="editable"
/> />
</div> </div>
</div> </div>