Add some basic result component layout [SCI-8959]

This commit is contained in:
Martin Artnik 2023-08-17 16:32:42 +02:00
parent c1cfb80de4
commit 23a8872e11
6 changed files with 146 additions and 86 deletions

View file

@ -1,10 +1,10 @@
# frozen_string_literal: true # frozen_string_literal: true
class ResultsController < ApplicationController class ResultsController < ApplicationController
skip_before_action :verify_authenticity_token, only: %i(create destroy) skip_before_action :verify_authenticity_token, only: %i(create update destroy)
before_action :load_my_module before_action :load_my_module
before_action :load_vars, only: %i(destroy elements assets upload_attachment update_view_state update_asset_view_mode ) before_action :load_vars, only: %i(destroy elements assets upload_attachment update_view_state update_asset_view_mode update)
before_action :check_destroy_permissions, only: :destroy before_action :check_destroy_permissions, only: :destroy
def index def index
@ -32,6 +32,12 @@ class ResultsController < ApplicationController
render json: result render json: result
end end
def update
@result.update!(result_params)
render json: @result
end
def elements def elements
render json: @result.result_orderable_elements.order(:position), render json: @result.result_orderable_elements.order(:position),
each_serializer: ResultOrderableElementSerializer, each_serializer: ResultOrderableElementSerializer,
@ -107,6 +113,10 @@ class ResultsController < ApplicationController
private private
def result_params
params.require(:result).permit(:name)
end
def apply_sort(results) def apply_sort(results)
case params[:sort] case params[:sort]
when 'updated_at_asc' when 'updated_at_asc'

View file

@ -1,85 +1,123 @@
<template> <template>
<div class="result-wrapper"> <div class="result-wrapper bg-white">
{{ result.id }} <div class="result-header flex justify-between p-3">
{{ result.attributes.name }} <div class="result-head-left">
<button @click="openReorderModal"> <InlineEdit
Open Rearrange Modal :value="result.attributes.name"
</button> :class="{ 'result-element--locked': !urls.update_url }"
<div class="inline-block"> :characterLimit="255"
<input type="file" class="hidden" ref="fileSelector" @change="loadFromComputer" multiple /> :allowBlank="false"
<div ref="elementsDropdownButton" v-if="urls.update_url" class="dropdown"> :attributeName="`${i18n.t('Result')} ${i18n.t('name')}`"
<button class="btn btn-light dropdown-toggle insert-button" type="button" :id="'resultInsertMenu_' + result.id" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true"> :autofocus="editingName"
{{ i18n.t('protocols.steps.insert.button') }} :placeholder="i18n.t('my_modules.results.placeholder')"
<span class="sn-icon sn-icon-down"></span> :defaultValue="i18n.t('my_modules.results.default_name')"
</button> @editingEnabled="editingName = true"
<ul ref="elementsDropdown" class="dropdown-menu insert-element-dropdown dropdown-menu-right" :aria-labelledby="'resultInsertMenu_' + result.id"> @editingDisabled="editingName = false"
<li class="title"> :editOnload="result.newResult == true"
{{ i18n.t('protocols.steps.insert.title') }} @update="updateName"
</li> />
<li class="action" @click="createElement('table')">
<i class="sn-icon sn-icon-tables"></i>
{{ i18n.t('protocols.steps.insert.table') }}
</li>
<li class="action dropdown-submenu-item">
<i class="sn-icon sn-icon-tables"></i>
{{ i18n.t('protocols.steps.insert.well_plate') }}
<span class="caret"></span>
<ul class="dropdown-submenu">
<li v-for="option in wellPlateOptions" :key="option.dimensions.toString()" class="action" @click="createElement('table', option.dimensions, true)">
{{ i18n.t(option.label) }}
</li>
</ul>
</li>
<li class="action" @click="createElement('text')">
<i class="sn-icon sn-icon-result-text"></i>
{{ i18n.t('protocols.steps.insert.text') }}
</li>
<li class="action dropdown-submenu-item">
<i class="sn-icon sn-icon-files"></i>
{{ i18n.t('protocols.steps.insert.attachment') }}
<span class="caret"></span>
<ul class="dropdown-submenu">
<li class="action" @click="openLoadFromComputer">
{{ i18n.t('protocols.steps.insert.add_file') }}
</li>
<li class="action" v-if="result.attributes.wopi_enabled" @click="openWopiFileModal">
{{ i18n.t('assets.create_wopi_file.button_text') }}
</li>
<li class="action" v-if="result.attributes.marvinjs_enabled" @click="openMarvinJsModal($refs.marvinJsButton)">
<span
class="new-marvinjs-upload-button text-sn-black text-decoration-none"
:data-object-id="result.id"
ref="marvinJsButton"
:data-marvin-url="result.attributes.marvinjs_context.marvin_js_asset_url"
:data-object-type="result.attributes.type"
tabindex="0"
>
{{ i18n.t('marvinjs.new_button') }}
</span>
</li>
</ul>
</li>
</ul>
</div> </div>
<div ref="actionsDropdownButton" class="dropdown"> <div class="result-head-right flex">
<button class="btn btn-light icon-btn dropdown-toggle insert-button" type="button" :id="'resultOptionsMenu_' + result.id" data-toggle="dropdown" data-display="static" aria-haspopup="true" aria-expanded="true"> <input type="file" class="hidden" ref="fileSelector" @change="loadFromComputer" multiple />
<i class="sn-icon sn-icon-more-hori"></i> <div ref="elementsDropdownButton" v-if="urls.update_url" class="dropdown">
</button> <button class="btn btn-light dropdown-toggle insert-button" type="button" :id="'resultInsertMenu_' + result.id" data-toggle="dropdown" aria-haspopup="true" aria-expanded="true">
<ul ref="actionsDropdown" class="dropdown-menu dropdown-menu-right insert-element-dropdown" :aria-labelledby="'resultOptionsMenu_' + result.id"> {{ i18n.t('protocols.steps.insert.button') }}
<li class="action" @click="openReorderModal"> <span class="sn-icon sn-icon-down"></span>
{{ i18n.t('my_modules.results.actions.rearrange') }} </button>
</li> <ul ref="elementsDropdown" class="dropdown-menu insert-element-dropdown dropdown-menu-right" :aria-labelledby="'resultInsertMenu_' + result.id">
<li class="action" @click="duplicateResult"> <li class="title">
{{ i18n.t('my_modules.results.actions.duplicate') }} <a>
</li> {{ i18n.t('protocols.steps.insert.title') }}
<li class="action" @click="archiveResult"> </a>
{{ i18n.t('my_modules.results.actions.archive') }} </li>
</li> <li class="action" @click="createElement('table')">
</ul> <a class="cursor-pointer">
<i class="sn-icon sn-icon-tables"></i>
{{ i18n.t('protocols.steps.insert.table') }}
</a>
</li>
<li class="action dropdown-submenu-item">
<a class="cursor-pointer">
<i class="sn-icon sn-icon-tables"></i>
{{ i18n.t('protocols.steps.insert.well_plate') }}
</a>
<span class="caret"></span>
<ul class="dropdown-submenu">
<li v-for="option in wellPlateOptions" :key="option.dimensions.toString()" class="action" @click="createElement('table', option.dimensions, true)">
<a class="cursor-pointer">
{{ i18n.t(option.label) }}
</a>
</li>
</ul>
</li>
<li class="action" @click="createElement('text')">
<a class="cursor-pointer">
<i class="sn-icon sn-icon-result-text"></i>
{{ i18n.t('protocols.steps.insert.text') }}
</a>
</li>
<li class="action dropdown-submenu-item">
<a class="cursor-pointer">
<i class="sn-icon sn-icon-files"></i>
{{ i18n.t('protocols.steps.insert.attachment') }}
</a>
<span class="caret"></span>
<ul class="dropdown-submenu">
<li class="action" @click="openLoadFromComputer">
<a class="cursor-pointer">
{{ i18n.t('protocols.steps.insert.add_file') }}
</a>
</li>
<li class="action" v-if="result.attributes.wopi_enabled" @click="openWopiFileModal">
<a class="cursor-pointer">
{{ i18n.t('assets.create_wopi_file.button_text') }}
</a>
</li>
<li class="action" v-if="result.attributes.marvinjs_enabled" @click="openMarvinJsModal($refs.marvinJsButton)">
<a class="cursor-point er">
<span
class="new-marvinjs-upload-button text-sn-black text-decoration-none"
:data-object-id="result.id"
ref="marvinJsButton"
:data-marvin-url="result.attributes.marvinjs_context.marvin_js_asset_url"
:data-object-type="result.attributes.type"
tabindex="0"
>
{{ i18n.t('marvinjs.new_button') }}
</span>
</a>
</li>
</ul>
</li>
</ul>
</div>
<a href="#"
ref="comments"
class="open-comments-sidebar btn icon-btn btn-light"
data-turbolinks="false"
data-object-type="Result"
:data-object-id="result.id">
<i class="sn-icon sn-icon-comments"></i>
</a>
<div ref="actionsDropdownButton" class="dropdown">
<button class="btn btn-light icon-btn dropdown-toggle insert-button" type="button" :id="'resultOptionsMenu_' + result.id" data-toggle="dropdown" data-display="static" aria-haspopup="true" aria-expanded="true">
<i class="sn-icon sn-icon-more-hori"></i>
</button>
<ul ref="actionsDropdown" class="dropdown-menu dropdown-menu-right insert-element-dropdown" :aria-labelledby="'resultOptionsMenu_' + result.id">
<li class="action" @click="openReorderModal">
<a class="cursor-pointer">{{ i18n.t('my_modules.results.actions.rearrange') }}</a>
</li>
<li class="action" @click="duplicateResult">
<a class="cursor-pointer">{{ i18n.t('my_modules.results.actions.duplicate') }}</a>
</li>
<li class="action" @click="archiveResult">
<a class="cursor-pointer">{{ i18n.t('my_modules.results.actions.archive') }}</a>
</li>
</ul>
</div>
</div> </div>
</div> </div>
<hr>
<ReorderableItemsModal v-if="reordering" <ReorderableItemsModal v-if="reordering"
title="Placeholder title for this modal" title="Placeholder title for this modal"
:items="reorderableElements" :items="reorderableElements"
@ -113,6 +151,7 @@
@attachments:viewMode="changeAttachmentsViewMode" @attachments:viewMode="changeAttachmentsViewMode"
@attachment:viewMode="updateAttachmentViewMode"/> @attachment:viewMode="updateAttachmentViewMode"/>
</div> </div>
<hr>
</div> </div>
</template> </template>
@ -122,6 +161,7 @@
import ResultTable from '../shared/content/table.vue'; import ResultTable from '../shared/content/table.vue';
import ResultText from '../shared/content/text.vue'; import ResultText from '../shared/content/text.vue';
import Attachments from '../shared/content/attachments.vue'; import Attachments from '../shared/content/attachments.vue';
import InlineEdit from '../shared/inline_edit.vue'
import AttachmentsMixin from '../shared/content/mixins/attachments.js' import AttachmentsMixin from '../shared/content/mixins/attachments.js'
import WopiFileModal from '../shared/content/attachments/mixins/wopi_file_modal.js' import WopiFileModal from '../shared/content/attachments/mixins/wopi_file_modal.js'
@ -146,7 +186,8 @@
{ label: 'protocols.steps.insert.well_plate_options.6_x_8', dimensions: [6, 8] }, { label: 'protocols.steps.insert.well_plate_options.6_x_8', dimensions: [6, 8] },
{ label: 'protocols.steps.insert.well_plate_options.6_x_4', dimensions: [6, 4] }, { label: 'protocols.steps.insert.well_plate_options.6_x_4', dimensions: [6, 4] },
{ label: 'protocols.steps.insert.well_plate_options.2_x_3', dimensions: [2, 3] } { label: 'protocols.steps.insert.well_plate_options.2_x_3', dimensions: [2, 3] }
] ],
editingName: false
} }
}, },
mixins: [UtilsMixin, AttachmentsMixin, WopiFileModal], mixins: [UtilsMixin, AttachmentsMixin, WopiFileModal],
@ -154,7 +195,8 @@
ReorderableItemsModal, ReorderableItemsModal,
ResultTable, ResultTable,
ResultText, ResultText,
Attachments Attachments,
InlineEdit
}, },
computed: { computed: {
reorderableElements() { reorderableElements() {
@ -278,7 +320,7 @@
$.post(this.urls[`create_${elementType}_url`], { tableDimensions: tableDimensions, plateTemplate: plateTemplate }, (result) => { $.post(this.urls[`create_${elementType}_url`], { tableDimensions: tableDimensions, plateTemplate: plateTemplate }, (result) => {
result.data.isNew = true; result.data.isNew = true;
this.elements.push(result.data) this.elements.push(result.data)
this.$emit('stepUpdated') this.$emit('resultUpdated')
}).fail(() => { }).fail(() => {
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger'); HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
}).done(() => { }).done(() => {
@ -298,6 +340,11 @@
}, },
duplicateResult() { duplicateResult() {
},
updateName(name) {
axios.patch(this.urls.update_url, { result: { name: name } }, (_) => {
this.$emit('resultUpdated')
});
} }
} }
} }

View file

@ -1,6 +1,6 @@
<template> <template>
<div class="results-wrapper"> <div class="results-wrapper">
<ResultsToolbar :sort="sort" @setSort="setSort" @newResult="createResult" @expandAll="expandAll" @collapseAll="collapseAll" lass="mb-3" /> <ResultsToolbar :sort="sort" @setSort="setSort" @newResult="createResult" @expandAll="expandAll" @collapseAll="collapseAll" class="mb-3" />
<div class="results-list"> <div class="results-list">
<Result v-for="result in results" :key="result.id" :result="result" /> <Result v-for="result in results" :key="result.id" :result="result" />
</div> </div>

View file

@ -28,7 +28,6 @@ class ResultSerializer < ActiveModel::Serializer
end end
end end
def updated_at def updated_at
object.updated_at.to_i object.updated_at.to_i
end end
@ -74,7 +73,7 @@ class ResultSerializer < ActiveModel::Serializer
if can_manage_result?(object) if can_manage_result?(object)
urls_list.merge!({ urls_list.merge!({
delete_url: result_path(object), delete_url: result_path(object),
update_url: result_path(object), update_url: my_module_result_path(object.my_module, object),
create_table_url: my_module_result_tables_path(object.my_module, object), create_table_url: my_module_result_tables_path(object.my_module, object),
create_text_url: my_module_result_texts_path(object.my_module, object), create_text_url: my_module_result_texts_path(object.my_module, object),
update_asset_view_mode_url: update_asset_view_mode_my_module_result_path(object.my_module, object), update_asset_view_mode_url: update_asset_view_mode_my_module_result_path(object.my_module, object),

View file

@ -14,6 +14,8 @@
<%= render partial: 'assets/wopi/create_wopi_file_modal' %> <%= render partial: 'assets/wopi/create_wopi_file_modal' %>
<% provide(:container_class, 'no-second-nav-container') %> <% provide(:container_class, 'no-second-nav-container') %>
<%= render partial: 'my_modules/header_actions' %>
<div id="results" data-behaviour="vue"> <div id="results" data-behaviour="vue">
<results url="<%= my_module_results_url(@my_module) %>"> <results url="<%= my_module_results_url(@my_module) %>">
</div> </div>

View file

@ -1240,6 +1240,8 @@ en:
load_from_file_protocol_general_error: "Failed to load the protocol from file. It is likely that certain fields (protocol and individual step titles and names) contain too many or too few characters.(max is %{max} and min is %{min})" load_from_file_protocol_general_error: "Failed to load the protocol from file. It is likely that certain fields (protocol and individual step titles and names) contain too many or too few characters.(max is %{max} and min is %{min})"
results: results:
head_title: "%{project} | %{module} | Results" head_title: "%{project} | %{module} | Results"
default_name: "New result"
placeholder: "Enter result name"
add_label: "New result" add_label: "New result"
new_text_result: "Text" new_text_result: "Text"
new_table_result: "Table" new_table_result: "Table"