Implement protocol options dropdown [SCI-6750, SCI-6751] (#4066)

* Implement protocol options dropdown [SCI-6750, SCI-6751]

* Implement simple API error handling for step status update [SCI-6354]
This commit is contained in:
artoscinote 2022-05-03 15:18:48 +02:00 committed by GitHub
parent ccc668ce5c
commit 6352a4dd04
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 176 additions and 48 deletions

View file

@ -27,4 +27,14 @@
}
}
}
.protocol-options-dropdown {
a {
cursor: pointer;
&.disabled {
cursor: default;
pointer-events: none;
}
}
}
}

View file

@ -263,14 +263,14 @@ class MyModulesController < ApplicationController
end
def protocol
render json: @my_module.protocol
render json: @my_module.protocol, serializer: ProtocolSerializer
end
def update_protocol
protocol = @my_module.protocol
protocol.update!(protocol_params)
render json: protocol
render json: protocol, serializer: ProtocolSerializer
end
def results

View file

@ -9,6 +9,7 @@ Vue.prototype.i18n = window.I18n;
window.initProtocolComponent = () => {
Vue.prototype.dateFormat = $('#protocolContainer').data('date-format');
new Vue({
el: '#protocolContainer',
components: {

View file

@ -6,8 +6,8 @@
<div class="task-section-title">
<h2>{{ i18n.t('Protocol') }}</h2>
</div>
<span v-if="protocol.linked" class="status-label linked">
[{{ protocol.name }}]
<span v-if="protocol.attributes.linked" class="status-label linked">
[{{ protocol.attributes.name }}]
</span>
<span class="status-label" v-else>
[{{ i18n.t('my_modules.protocols.protocol_status_bar.unlinked') }}]
@ -16,52 +16,20 @@
<div class="sci-btn-group actions-block">
<a class="btn btn-primary" @click="addStep(steps.length)">
<span class="fas fa-plus" aria-hidden="true"></span>
<span>New step</span>
<span>{{ i18n.t("protocols.steps.new_step") }}</span>
</a>
<a class="btn btn-default" data-toggle="modal" data-target="#print-protocol-modal">
<span class="fas fa-print" aria-hidden="true"></span>
<span>Print</span>
<span>{{ i18n.t("protocols.print.button") }}</span>
</a>
<div class="dropdown sci-dropdown">
<button class="btn btn-secondary dropdown-toggle" type="button" id="dropdownProtocolOptions" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<span class="fas fa-cog"></span>
<span>Protocol options</span>
<span class="caret"></span>
</button>
<ul class="dropdown-menu dropdown-menu-right" aria-labelledby="dropdownProtocolOptions">
<li>
<a>
<span class="fas fa-edit"></span>
<span>Load from repository</span>
</a>
</li>
<li>
<a>
<span class="fas fa-download"></span>
<span>Import protocol</span>
</a>
</li>
<li>
<a>
<span class="fas fa-upload"></span>
<span>Export protocol</span>
</a>
</li>
<li>
<a>
<span class="fas fa-save"></span>
<span>Save to repository</span>
</a>
</li>
</ul>
</div>
<ProtocolOptions v-if="protocol.attributes && protocol.attributes.urls" :protocol="protocol" />
</div>
</div>
<div v-if="protocol.id" id="protocol-content" class="protocol-content collapse in" aria-expanded="true">
<div class="protocol-description">
<div class="protocol-name">
<InlineEdit
:value="protocol.name"
:value="protocol.attributes.name"
:characterLimit="255"
:placeholder="i18n.t('my_modules.protocols.protocol_status_bar.enter_name')"
:allowBlank="true"
@ -95,6 +63,7 @@
<script>
import InlineEdit from 'vue/shared/inline_edit.vue'
import Step from 'vue/protocol/step'
import ProtocolOptions from 'vue/protocol/protocolOptions'
export default {
name: 'ProtocolContainer',
@ -116,16 +85,18 @@
required: true
}
},
components: { Step, InlineEdit },
components: { Step, InlineEdit, ProtocolOptions },
data() {
return {
protocol: {},
protocol: {
attributes: {}
},
steps: {}
}
},
created() {
$.get(this.protocolUrl, (data) => {
this.protocol = data;
$.get(this.protocolUrl, (result) => {
this.protocol = result.data;
});
$.get(this.stepsUrl, (result) => {
this.steps = result.data
@ -133,7 +104,7 @@
},
methods: {
updateName(newName) {
this.protocol.name = newName;
this.protocol.attributes.name = newName;
$.ajax({
type: 'PATCH',
url: this.protocolUrl,

View file

@ -0,0 +1,98 @@
<template>
<div class="dropdown sci-dropdown protocol-options-dropdown">
<button
class="btn btn-secondary dropdown-toggle"
type="button"
id="dropdownProtocolOptions"
data-toggle="dropdown"
aria-haspopup="true"
aria-expanded="true"
>
<span class="fas fa-cog"></span>
<span>{{ i18n.t("my_modules.protocol.options_dropdown.title") }}</span>
<span class="caret"></span>
</button>
<ul
class="dropdown-menu dropdown-menu-right"
aria-labelledby="dropdownProtocolOptions"
>
<li>
<a
ref="loadProtocol"
data-action="load-from-repository"
@click="loadProtocol"
:class="{ disabled: !protocol.attributes.urls.load_from_repo_url }"
>
<span class="fas fa-edit"></span>
<span>{{ i18n.t("my_modules.protocol.options_dropdown.load_from_repo") }}</span>
</a>
</li>
<li>
<a
ref="saveProtocol"
data-action="copy-to-repository"
@click="saveProtocol"
:class="{ disabled: !protocol.attributes.urls.save_to_repo_url }"
>
<span class="fas fa-check"></span>
<span>{{
i18n.t("my_modules.protocol.options_dropdown.save_to_repo")
}}</span>
</a>
</li>
<li>
<a data-action="load-from-file"
class="btn-open-file"
:class="{ disabled: !protocol.attributes.urls.import_url }">
<span class="fas fa-download"></span>
<span>{{ i18n.t("my_modules.protocol.options_dropdown.import") }}</span>
<input type="file" value="" accept=".eln" data-turbolinks="false">
</a>
</li>
<li>
<a
data-turbolinks="false"
:href="protocol.attributes.urls.export_url"
:class="{ disabled: !protocol.attributes.urls.export_url }"
>
<span class="fas fa-upload"></span>
<span>{{
i18n.t("my_modules.protocol.options_dropdown.export")
}}</span>
</a>
</li>
</ul>
</div>
</template>
<script>
export default {
name: "ProtocolOptions",
props: {
protocol: {
type: Object,
required: true,
},
},
mounted() {
// Legacy global functions from app/assets/javascripts/my_modules/protocols.js
initLoadFromRepository();
initCopyToRepository();
initImport();
},
methods: {
loadProtocol() {
$.get(
this.protocol.attributes.urls.load_from_repo_url + "?type=recent"
).success((data) => {
$(this.$refs.loadProtocol).trigger("ajax:success", data);
});
},
saveProtocol() {
$.get(this.protocol.attributes.urls.save_to_repo_url).success((data) => {
$(this.$refs.saveProtocol).trigger("ajax:success", data);
});
}
},
};
</script>

View file

@ -107,8 +107,12 @@
});
},
changeState() {
$.post(this.step.attributes.urls.state_url, {completed: !this.step.attributes.completed}, (result) => {
this.$emit('step:update', result.data.attributes)
this.step.attributes.completed = !this.step.attributes.completed;
this.$emit('step:update', this.step)
$.post(this.step.attributes.urls.state_url, {completed: this.step.attributes.completed}).error(() => {
this.step.attributes.completed = !this.step.attributes.completed;
this.$emit('step:update', this.step)
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
})
},
updateName(newName) {

View file

@ -0,0 +1,43 @@
# frozen_string_literal: true
class ProtocolSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers
attributes :name, :id, :urls
def urls
{
load_from_repo_url: load_from_repo_url,
save_to_repo_url: save_to_repo_url,
export_url: export_url,
import_url: import_url
}
end
private
def load_from_repo_url
return unless can_manage_protocol_in_module?(object)
load_from_repository_modal_protocol_path(object, format: :json)
end
def save_to_repo_url
return unless can_read_protocol_in_module?(object) && can_create_protocols_in_repository?(object.team)
copy_to_repository_modal_protocol_path(object, format: :json)
end
def import_url
return unless can_manage_protocol_in_module?(object)
load_from_file_protocol_path(object, format: :json)
end
def export_url
return unless can_read_protocol_in_module?(object)
export_protocols_path(protocol_ids: object.id, my_module_id: object.my_module.id)
end
end

View file

@ -178,7 +178,8 @@ en:
attributes:
output_id:
creates_cycle: "mustn't create cycle"
errors:
general: "Something went wrong."
helpers:
label:
team: