mirror of
https://github.com/scinote-eln/scinote-web.git
synced 2025-10-09 21:36:44 +08:00
Add elements and attachments to new result [SCI-8953]
This commit is contained in:
parent
ac37885885
commit
f6191dbb67
14 changed files with 531 additions and 23 deletions
56
app/controllers/result_elements/base_controller.rb
Normal file
56
app/controllers/result_elements/base_controller.rb
Normal file
|
@ -0,0 +1,56 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module ResultElements
|
||||||
|
class BaseController < ApplicationController
|
||||||
|
before_action :load_result_and_my_module
|
||||||
|
before_action :check_manage_permissions
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def load_result_and_my_module
|
||||||
|
@result = Result.find_by(id: params[:result_id])
|
||||||
|
return render_404 unless @result
|
||||||
|
|
||||||
|
@my_module = @result.my_module
|
||||||
|
end
|
||||||
|
|
||||||
|
def check_manage_permissions
|
||||||
|
render_403 unless can_manage_my_module?(@my_module)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_in_result!(result, new_orderable)
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
new_orderable.save!
|
||||||
|
|
||||||
|
result.result_orderable_elements.create!(
|
||||||
|
position: result.result_orderable_elements.length,
|
||||||
|
orderable: new_orderable
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def render_result_orderable_element(orderable)
|
||||||
|
result_orderable_element = orderable.result_orderable_element
|
||||||
|
render json: result_orderable_element, serializer: ResultOrderableElementSerializer, user: current_user
|
||||||
|
end
|
||||||
|
|
||||||
|
def log_step_activity(element_type_of, message_items)
|
||||||
|
# TODO
|
||||||
|
#message_items[:my_module] = @protocol.my_module.id if @protocol.in_module?
|
||||||
|
#Activities::CreateActivityService.call(
|
||||||
|
# activity_type: "#{!@step.protocol.in_module? ? 'protocol_step_' : 'task_step_'}#{element_type_of}",
|
||||||
|
# owner: current_user,
|
||||||
|
# team: @protocol.team,
|
||||||
|
# project: @protocol.in_module? ? @protocol.my_module.project : nil,
|
||||||
|
# subject: @protocol,
|
||||||
|
# message_items: {
|
||||||
|
# step: @step.id,
|
||||||
|
# step_position: {
|
||||||
|
# id: @step.id,
|
||||||
|
# value_for: 'position_plus_one'
|
||||||
|
# },
|
||||||
|
# }.merge(message_items)
|
||||||
|
#)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
99
app/controllers/result_elements/tables_controller.rb
Normal file
99
app/controllers/result_elements/tables_controller.rb
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module ResultElements
|
||||||
|
class TablesController < BaseController
|
||||||
|
before_action :load_table, only: %i(update destroy duplicate)
|
||||||
|
|
||||||
|
def create
|
||||||
|
predefined_table_dimensions = create_table_params[:tableDimensions].map(&:to_i)
|
||||||
|
name = if predefined_table_dimensions[0] == predefined_table_dimensions[1]
|
||||||
|
t('protocols.steps.table.default_name',
|
||||||
|
position: @step.step_tables.length + 1)
|
||||||
|
else
|
||||||
|
t('protocols.steps.plate.default_name',
|
||||||
|
position: @step.step_tables.length + 1)
|
||||||
|
end
|
||||||
|
result_table = @result.result_tables.new(table:
|
||||||
|
Table.new(
|
||||||
|
name: name,
|
||||||
|
contents: { data: Array.new(predefined_table_dimensions[0],
|
||||||
|
Array.new(predefined_table_dimensions[1], '')) }.to_json,
|
||||||
|
metadata: { plateTemplate: create_table_params[:plateTemplate] == 'true' },
|
||||||
|
created_by: current_user,
|
||||||
|
team: @my_module.team
|
||||||
|
))
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
create_in_step!(@step, step_table)
|
||||||
|
# log_step_activity(:table_added, { table_name: step_table.table.name })
|
||||||
|
end
|
||||||
|
|
||||||
|
render_result_orderable_element(step_table)
|
||||||
|
rescue ActiveRecord::RecordInvalid
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
@table.assign_attributes(table_params.except(:metadata))
|
||||||
|
begin
|
||||||
|
if table_params[:metadata].present?
|
||||||
|
|
||||||
|
@table.metadata = if @table.metadata
|
||||||
|
@table.metadata.merge(JSON.parse(table_params[:metadata]))
|
||||||
|
else
|
||||||
|
JSON.parse(table_params[:metadata])
|
||||||
|
end
|
||||||
|
end
|
||||||
|
rescue JSON::ParserError
|
||||||
|
@table.metadata = {}
|
||||||
|
end
|
||||||
|
@table.save!
|
||||||
|
#log_step_activity(:table_edited, { table_name: @table.name })
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: @table, serializer: ResultTableSerializer, user: current_user
|
||||||
|
rescue ActiveRecord::RecordInvalid
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
if @table.destroy
|
||||||
|
#log_step_activity(:table_deleted, { table_name: @table.name })
|
||||||
|
head :ok
|
||||||
|
else
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def duplicate
|
||||||
|
#ActiveRecord::Base.transaction do
|
||||||
|
# position = @table.step_table.step_orderable_element.position
|
||||||
|
# @step.step_orderable_elements.where('position > ?', position).order(position: :desc).each do |element|
|
||||||
|
# element.update(position: element.position + 1)
|
||||||
|
# end
|
||||||
|
# @table.name += ' (1)'
|
||||||
|
# new_table = @table.duplicate(@step, current_user, position + 1)
|
||||||
|
# log_step_activity(:table_duplicated, { table_name: new_table.name })
|
||||||
|
# render_step_orderable_element(new_table.step_table)
|
||||||
|
#end
|
||||||
|
rescue ActiveRecord::RecordInvalid
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def table_params
|
||||||
|
params.permit(:name, :contents, :metadata)
|
||||||
|
end
|
||||||
|
|
||||||
|
def create_table_params
|
||||||
|
params.permit(:plateTemplate, tableDimensions: [])
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_table
|
||||||
|
@table = @result.tables.find_by(id: params[:id])
|
||||||
|
return render_404 unless @table
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
95
app/controllers/result_elements/texts_controller.rb
Normal file
95
app/controllers/result_elements/texts_controller.rb
Normal file
|
@ -0,0 +1,95 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module ResultElements
|
||||||
|
class TextsController < BaseController
|
||||||
|
include ActionView::Helpers::UrlHelper
|
||||||
|
include ApplicationHelper
|
||||||
|
include InputSanitizeHelper
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
|
before_action :load_result_text, only: %i(update destroy duplicate)
|
||||||
|
|
||||||
|
def create
|
||||||
|
result_text = @result.result_texts.build
|
||||||
|
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
create_in_step!(@result, result_text)
|
||||||
|
#log_step_activity(:text_added, { text_name: step_text.name })
|
||||||
|
end
|
||||||
|
|
||||||
|
render_result_orderable_element(result_text)
|
||||||
|
rescue ActiveRecord::RecordInvalid
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def update
|
||||||
|
old_text = @result_text.text
|
||||||
|
ActiveRecord::Base.transaction do
|
||||||
|
@result_text.update!(result_text_params)
|
||||||
|
TinyMceAsset.update_images(@result_text, params[:tiny_mce_images], current_user)
|
||||||
|
#log_step_activity(:text_edited, { text_name: @step_text.name })
|
||||||
|
result_annotation_notification(old_text)
|
||||||
|
end
|
||||||
|
|
||||||
|
render json: @result_text, serializer: ResultTextSerializer, user: current_user
|
||||||
|
rescue ActiveRecord::RecordInvalid
|
||||||
|
render json: @result_text.errors, status: :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
def destroy
|
||||||
|
if @result_text.destroy
|
||||||
|
log_step_activity(:text_deleted, { text_name: @result_text.name })
|
||||||
|
head :ok
|
||||||
|
else
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def duplicate
|
||||||
|
#ActiveRecord::Base.transaction do
|
||||||
|
# position = @step_text.step_orderable_element.position
|
||||||
|
# @step.step_orderable_elements.where('position > ?', position).order(position: :desc).each do |element|
|
||||||
|
# element.update(position: element.position + 1)
|
||||||
|
# end
|
||||||
|
# new_step_text = @step_text.duplicate(@step, position + 1)
|
||||||
|
# log_step_activity(:text_duplicated, { text_name: new_step_text.name })
|
||||||
|
# render_step_orderable_element(new_step_text)
|
||||||
|
#end
|
||||||
|
rescue ActiveRecord::RecordInvalid
|
||||||
|
head :unprocessable_entity
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def result_text_params
|
||||||
|
params.require(:text_component).permit(:text)
|
||||||
|
end
|
||||||
|
|
||||||
|
def load_result_text
|
||||||
|
@result_text = @result.result_texts.find_by(id: params[:id])
|
||||||
|
return render_404 unless @result_text
|
||||||
|
end
|
||||||
|
|
||||||
|
def result_annotation_notification(old_text = nil)
|
||||||
|
smart_annotation_notification(
|
||||||
|
old_text: (old_text if old_text),
|
||||||
|
new_text: @result_text.text,
|
||||||
|
title: t('notifications.result_annotation_title',
|
||||||
|
result: @result.name,
|
||||||
|
user: current_user.full_name),
|
||||||
|
message: t('notifications.result_annotation_message_html',
|
||||||
|
project: link_to(@result.my_module.experiment.project.name,
|
||||||
|
project_url(@result.my_module
|
||||||
|
.experiment
|
||||||
|
.project)),
|
||||||
|
experiment: link_to(@result.my_module.experiment.name,
|
||||||
|
my_modules_experiment_url(@result.my_module
|
||||||
|
.experiment)),
|
||||||
|
my_module: link_to(@result.my_module.name,
|
||||||
|
protocols_my_module_url(
|
||||||
|
@result.my_module
|
||||||
|
)))
|
||||||
|
)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -4,6 +4,7 @@ import Results from '../../vue/results/results.vue';
|
||||||
|
|
||||||
Vue.use(TurbolinksAdapter);
|
Vue.use(TurbolinksAdapter);
|
||||||
Vue.prototype.i18n = window.I18n;
|
Vue.prototype.i18n = window.I18n;
|
||||||
|
Vue.prototype.ActiveStoragePreviews = window.ActiveStoragePreviews;
|
||||||
|
|
||||||
new Vue({
|
new Vue({
|
||||||
el: '#results',
|
el: '#results',
|
||||||
|
|
|
@ -222,7 +222,7 @@
|
||||||
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue'
|
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue'
|
||||||
|
|
||||||
import UtilsMixin from '../mixins/utils.js'
|
import UtilsMixin from '../mixins/utils.js'
|
||||||
import AttachmentsMixin from './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'
|
||||||
import StorageUsage from '../shared/content/attachments/storage_usage.vue'
|
import StorageUsage from '../shared/content/attachments/storage_usage.vue'
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,68 @@
|
||||||
<button @click="openReorderModal">
|
<button @click="openReorderModal">
|
||||||
Open Rearrange Modal
|
Open Rearrange Modal
|
||||||
</button>
|
</button>
|
||||||
|
<div>
|
||||||
|
<input type="file" class="hidden" ref="fileSelector" @change="loadFromComputer" multiple />
|
||||||
|
<div ref="elementsDropdownButton" 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">
|
||||||
|
{{ i18n.t('protocols.steps.insert.button') }}
|
||||||
|
<span class="sn-icon sn-icon-down"></span>
|
||||||
|
</button>
|
||||||
|
<ul ref="elementsDropdown" class="dropdown-menu insert-element-dropdown dropdown-menu-right" :aria-labelledby="'stepInserMenu_' + step.id">
|
||||||
|
<li class="title">
|
||||||
|
{{ i18n.t('protocols.steps.insert.title') }}
|
||||||
|
</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('checklist')">
|
||||||
|
<i class="sn-icon sn-icon-activities"></i>
|
||||||
|
{{ i18n.t('protocols.steps.insert.checklist') }}
|
||||||
|
</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="step.attributes.wopi_enabled" @click="openWopiFileModal">
|
||||||
|
{{ i18n.t('assets.create_wopi_file.button_text') }}
|
||||||
|
</li>
|
||||||
|
<li class="action" v-if="step.attributes.marvinjs_enabled" @click="openMarvinJsModal($refs.marvinJsButton)">
|
||||||
|
<span
|
||||||
|
class="new-marvinjs-upload-button text-sn-black text-decoration-none"
|
||||||
|
:data-object-id="step.id"
|
||||||
|
ref="marvinJsButton"
|
||||||
|
:data-marvin-url="step.attributes.marvinjs_context.marvin_js_asset_url"
|
||||||
|
:data-object-type="step.attributes.type"
|
||||||
|
tabindex="0"
|
||||||
|
>
|
||||||
|
{{ i18n.t('marvinjs.new_button') }}
|
||||||
|
</span>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
<hr>
|
<hr>
|
||||||
<ReorderableItemsModal v-if="reordering"
|
<ReorderableItemsModal v-if="reordering"
|
||||||
title="Placeholder title for this modal"
|
title="Placeholder title for this modal"
|
||||||
|
@ -12,12 +74,46 @@
|
||||||
@reorder="updateElementOrder"
|
@reorder="updateElementOrder"
|
||||||
@close="closeReorderModal"
|
@close="closeReorderModal"
|
||||||
/>
|
/>
|
||||||
|
<div>
|
||||||
|
<template v-for="(element, index) in orderedElements">
|
||||||
|
<component
|
||||||
|
:is="elements[index].attributes.orderable_type"
|
||||||
|
:key="index"
|
||||||
|
:element.sync="elements[index]"
|
||||||
|
:inRepository="false"
|
||||||
|
:reorderElementUrl="elements.length > 1 ? urls.reorder_elements_url : ''"
|
||||||
|
:assignableMyModuleId="result.attributes.my_module_id"
|
||||||
|
:isNew="element.isNew"
|
||||||
|
@component:delete="deleteElement"
|
||||||
|
@update="updateElement"
|
||||||
|
@reorder="openReorderModal"
|
||||||
|
@component:insert="insertElement"
|
||||||
|
/>
|
||||||
|
</template>
|
||||||
|
<Attachments v-if="attachments.length"
|
||||||
|
:parent="result"
|
||||||
|
:attachments="attachments"
|
||||||
|
:attachmentsReady="attachmentsReady"
|
||||||
|
@attachments:openFileModal="showFileModal = true"
|
||||||
|
@attachment:deleted="attachmentDeleted"
|
||||||
|
@attachment:uploaded="loadAttachments"
|
||||||
|
@attachments:order="() => {}"
|
||||||
|
@attachments:viewMode="() => {}"
|
||||||
|
@attachment:viewMode="() => {}"/>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script>
|
<script>
|
||||||
import axios from 'axios';
|
import axios from 'axios';
|
||||||
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue'
|
import ReorderableItemsModal from '../shared/reorderable_items_modal.vue';
|
||||||
|
import ResultTable from '../shared/content/table.vue';
|
||||||
|
import ResultText from '../shared/content/text.vue';
|
||||||
|
import Attachments from '../shared/content/attachments.vue';
|
||||||
|
|
||||||
|
import AttachmentsMixin from '../shared/content/mixins/attachments.js'
|
||||||
|
import WopiFileModal from '../shared/content/attachments/mixins/wopi_file_modal.js'
|
||||||
|
import UtilsMixin from '../mixins/utils.js'
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
name: 'Results',
|
name: 'Results',
|
||||||
|
@ -27,11 +123,18 @@
|
||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
reordering: false,
|
reordering: false,
|
||||||
elements: []
|
elements: [],
|
||||||
|
attachments: [],
|
||||||
|
attachmentsReady: false,
|
||||||
|
showFileModal: false
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
mixins: [UtilsMixin, AttachmentsMixin, WopiFileModal],
|
||||||
components: {
|
components: {
|
||||||
ReorderableItemsModal
|
ReorderableItemsModal,
|
||||||
|
ResultTable,
|
||||||
|
ResultText,
|
||||||
|
Attachments
|
||||||
},
|
},
|
||||||
computed: {
|
computed: {
|
||||||
reorderableElements() {
|
reorderableElements() {
|
||||||
|
@ -44,6 +147,10 @@
|
||||||
return this.result.attributes.urls || {}
|
return this.result.attributes.urls || {}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
created() {
|
||||||
|
this.loadAttachments();
|
||||||
|
this.loadElements();
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
openReorderModal() {
|
openReorderModal() {
|
||||||
this.reordering = true;
|
this.reordering = true;
|
||||||
|
@ -76,6 +183,37 @@
|
||||||
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
|
deleteElement(element) {
|
||||||
|
},
|
||||||
|
updateElement(element) {
|
||||||
|
},
|
||||||
|
insertElement(element) {
|
||||||
|
},
|
||||||
|
loadElements() {
|
||||||
|
$.get(this.urls.elements_url, (result) => {
|
||||||
|
this.elements = result.data
|
||||||
|
});
|
||||||
|
},
|
||||||
|
loadAttachments() {
|
||||||
|
this.attachmentsReady = false
|
||||||
|
|
||||||
|
$.get(this.urls.attachments_url, (result) => {
|
||||||
|
this.attachments = result.data
|
||||||
|
|
||||||
|
if (this.attachments.findIndex((e) => e.attributes.attached === false) >= 0) {
|
||||||
|
setTimeout(() => {
|
||||||
|
this.loadAttachments()
|
||||||
|
}, 10000)
|
||||||
|
} else {
|
||||||
|
this.attachmentsReady = true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
this.showFileModal = false;
|
||||||
|
},
|
||||||
|
attachmentDeleted(id) {
|
||||||
|
this.attachments = this.attachments.filter((a) => a.id !== id );
|
||||||
|
this.$emit('resultUpdated');
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
|
@ -10,6 +10,14 @@ export default {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
|
computed: {
|
||||||
|
attachmentsParent() {
|
||||||
|
return this.step || this.result;
|
||||||
|
},
|
||||||
|
attachmentsParentName() {
|
||||||
|
return this.step ? 'step' : 'result';
|
||||||
|
}
|
||||||
|
},
|
||||||
methods: {
|
methods: {
|
||||||
dropFile(e) {
|
dropFile(e) {
|
||||||
if (!this.showFileModal && e.dataTransfer && e.dataTransfer.files.length) {
|
if (!this.showFileModal && e.dataTransfer && e.dataTransfer.files.length) {
|
||||||
|
@ -30,7 +38,7 @@ export default {
|
||||||
button.click();
|
button.click();
|
||||||
},
|
},
|
||||||
openWopiFileModal() {
|
openWopiFileModal() {
|
||||||
this.initWopiFileModal(this.step, (_e, data, status) => {
|
this.initWopiFileModal(this.attachmentsParent, (_e, data, status) => {
|
||||||
if (status === 'success') {
|
if (status === 'success') {
|
||||||
this.addAttachment(data)
|
this.addAttachment(data)
|
||||||
} else {
|
} else {
|
||||||
|
@ -43,17 +51,17 @@ export default {
|
||||||
let filesUploadedCntr = 0;
|
let filesUploadedCntr = 0;
|
||||||
this.showFileModal = false;
|
this.showFileModal = false;
|
||||||
|
|
||||||
if (!this.step.attributes.urls.upload_attachment_url) return false;
|
if (!this.attachmentsParent.attributes.urls.upload_attachment_url) return false;
|
||||||
|
|
||||||
return new Promise((resolve, reject) => {
|
return new Promise((resolve, reject) => {
|
||||||
$(files).each((_, file) => {
|
$(files).each((_, file) => {
|
||||||
const fileObject = {
|
const fileObject = {
|
||||||
attributes: {
|
attributes: {
|
||||||
progress: 0,
|
progress: 0,
|
||||||
view_mode: this.step.attributes.assets_view_mode,
|
view_mode: this.attachmentsParent.attributes.assets_view_mode,
|
||||||
file_name: file.name,
|
file_name: file.name,
|
||||||
uploading: true,
|
uploading: true,
|
||||||
asset_order: this.viewModeOrder[this.step.attributes.assets_view_mode]
|
asset_order: this.viewModeOrder[this.attachmentsParent.attributes.assets_view_mode]
|
||||||
},
|
},
|
||||||
directUploadWillStoreFileWithXHR(request) {
|
directUploadWillStoreFileWithXHR(request) {
|
||||||
request.upload.addEventListener('progress', (e) => {
|
request.upload.addEventListener('progress', (e) => {
|
||||||
|
@ -68,16 +76,16 @@ export default {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const storageLimit = this.step.attributes.storage_limit &&
|
const storageLimit = this.attachmentsParent.attributes.storage_limit &&
|
||||||
this.step.attributes.storage_limit.total > 0 &&
|
this.attachmentsParent.attributes.storage_limit.total > 0 &&
|
||||||
this.step.attributes.storage_limit.used >= this.step.attributes.storage_limit.total;
|
this.attachmentsParent.attributes.storage_limit.used >= this.attachmentsParent.attributes.storage_limit.total;
|
||||||
if (storageLimit) {
|
if (storageLimit) {
|
||||||
fileObject.error = I18n.t('protocols.steps.attachments.new.no_more_space');
|
fileObject.error = I18n.t('protocols.steps.attachments.new.no_more_space');
|
||||||
this.attachments.push(fileObject);
|
this.attachments.push(fileObject);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const upload = new ActiveStorage.DirectUpload(file, this.step.attributes.urls.direct_upload_url, fileObject);
|
const upload = new ActiveStorage.DirectUpload(file, this.attachmentsParent.attributes.urls.direct_upload_url, fileObject);
|
||||||
|
|
||||||
fileObject.isNewUpload = true;
|
fileObject.isNewUpload = true;
|
||||||
this.attachments.push(fileObject);
|
this.attachments.push(fileObject);
|
||||||
|
@ -93,7 +101,7 @@ export default {
|
||||||
reject(error);
|
reject(error);
|
||||||
} else {
|
} else {
|
||||||
const signedId = blob.signed_id;
|
const signedId = blob.signed_id;
|
||||||
$.post(this.step.attributes.urls.upload_attachment_url, {
|
$.post(this.attachmentsParent.attributes.urls.upload_attachment_url, {
|
||||||
signed_blob_id: signedId
|
signed_blob_id: signedId
|
||||||
}, (result) => {
|
}, (result) => {
|
||||||
fileObject.id = result.data.id;
|
fileObject.id = result.data.id;
|
||||||
|
@ -108,7 +116,7 @@ export default {
|
||||||
filesUploadedCntr += 1;
|
filesUploadedCntr += 1;
|
||||||
if (filesUploadedCntr === filesToUploadCntr) {
|
if (filesUploadedCntr === filesToUploadCntr) {
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
this.$emit('stepUpdated');
|
this.$emit(`${this.attachmentsParentName}Updated`);
|
||||||
}, 1000);
|
}, 1000);
|
||||||
resolve('done');
|
resolve('done');
|
||||||
}
|
}
|
||||||
|
@ -118,18 +126,18 @@ export default {
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
changeAttachmentsOrder(order) {
|
changeAttachmentsOrder(order) {
|
||||||
this.step.attributes.assets_order = order;
|
this.attachmentsParent.attributes.assets_order = order;
|
||||||
$.post(this.step.attributes.urls.update_view_state_step_url, {
|
$.post(this.attachmentsParent.attributes.urls.update_view_state_url, {
|
||||||
assets: { order }
|
assets: { order }
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
changeAttachmentsViewMode(viewMode) {
|
changeAttachmentsViewMode(viewMode) {
|
||||||
this.step.attributes.assets_view_mode = viewMode;
|
this.attachmentsParent.attributes.assets_view_mode = viewMode;
|
||||||
this.attachments.forEach((attachment) => {
|
this.attachments.forEach((attachment) => {
|
||||||
this.$set(attachment.attributes, 'view_mode', viewMode);
|
this.$set(attachment.attributes, 'view_mode', viewMode);
|
||||||
this.$set(attachment.attributes, 'asset_order', this.viewModeOrder[viewMode]);
|
this.$set(attachment.attributes, 'asset_order', this.viewModeOrder[viewMode]);
|
||||||
});
|
});
|
||||||
$.post(this.step.attributes.urls.update_asset_view_mode_url, {
|
$.post(this.attachmentsParent.attributes.urls.update_asset_view_mode_url, {
|
||||||
assets_view_mode: viewMode
|
assets_view_mode: viewMode
|
||||||
});
|
});
|
||||||
},
|
},
|
14
app/serializers/result_orderable_element_serializer.rb
Normal file
14
app/serializers/result_orderable_element_serializer.rb
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ResultOrderableElementSerializer < ActiveModel::Serializer
|
||||||
|
attributes :position, :orderable, :orderable_type
|
||||||
|
|
||||||
|
def orderable
|
||||||
|
case object.orderable_type
|
||||||
|
when 'ResultTable'
|
||||||
|
ResultTableSerializer.new(object.orderable.table, scope: { user: @instance_options[:user] }).as_json
|
||||||
|
when 'ResultText'
|
||||||
|
ResultTextSerializer.new(object.orderable, scope: { user: @instance_options[:user] }).as_json
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,7 +7,7 @@ class ResultSerializer < ActiveModel::Serializer
|
||||||
include ActionView::Helpers::TextHelper
|
include ActionView::Helpers::TextHelper
|
||||||
include InputSanitizeHelper
|
include InputSanitizeHelper
|
||||||
|
|
||||||
attributes :name, :id, :urls, :updated_at, :created_at_formatted, :updated_at_formatted, :user
|
attributes :name, :id, :urls, :updated_at, :created_at_formatted, :updated_at_formatted, :user, :my_module_id
|
||||||
|
|
||||||
def updated_at
|
def updated_at
|
||||||
object.updated_at.to_i
|
object.updated_at.to_i
|
||||||
|
@ -30,7 +30,8 @@ class ResultSerializer < ActiveModel::Serializer
|
||||||
|
|
||||||
def urls
|
def urls
|
||||||
{
|
{
|
||||||
|
elements_url: elements_my_module_result_path(object.my_module, object),
|
||||||
|
attachments_url: assets_my_module_result_path(object.my_module, object)
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
33
app/serializers/result_table_serializer.rb
Normal file
33
app/serializers/result_table_serializer.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ResultTableSerializer < ActiveModel::Serializer
|
||||||
|
include Canaid::Helpers::PermissionsHelper
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
|
|
||||||
|
attributes :name, :contents, :urls, :icon, :metadata
|
||||||
|
|
||||||
|
def contents
|
||||||
|
object.contents_utf_8
|
||||||
|
end
|
||||||
|
|
||||||
|
def icon
|
||||||
|
'fa-table'
|
||||||
|
end
|
||||||
|
|
||||||
|
def urls
|
||||||
|
return if object.destroyed?
|
||||||
|
|
||||||
|
object.reload unless object.result
|
||||||
|
|
||||||
|
p object.result
|
||||||
|
p scope[:user] || @instance_options[:user]
|
||||||
|
p can_manage_result?(scope[:user] || @instance_options[:user], object.result)
|
||||||
|
return {} unless can_manage_result?(scope[:user] || @instance_options[:user], object.result)
|
||||||
|
|
||||||
|
{
|
||||||
|
duplicate_url: duplicate_my_module_result_table_path(object.result.my_module, object.result, object),
|
||||||
|
delete_url: my_module_result_table_path(object.result.my_module, object.result, object),
|
||||||
|
update_url: my_module_result_table_path(object.result.my_module, object.result, object)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
46
app/serializers/result_text_serializer.rb
Normal file
46
app/serializers/result_text_serializer.rb
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class ResultTextSerializer < ActiveModel::Serializer
|
||||||
|
include Canaid::Helpers::PermissionsHelper
|
||||||
|
include Rails.application.routes.url_helpers
|
||||||
|
include ApplicationHelper
|
||||||
|
include ActionView::Helpers::TextHelper
|
||||||
|
|
||||||
|
attributes :id, :text, :urls, :text_view, :icon, :placeholder
|
||||||
|
|
||||||
|
def updated_at
|
||||||
|
object.updated_at.to_i
|
||||||
|
end
|
||||||
|
|
||||||
|
def placeholder
|
||||||
|
I18n.t('protocols.steps.text.placeholder')
|
||||||
|
end
|
||||||
|
|
||||||
|
def text_view
|
||||||
|
@user = scope[:user]
|
||||||
|
custom_auto_link(object.tinymce_render('text'),
|
||||||
|
simple_format: false,
|
||||||
|
tags: %w(img),
|
||||||
|
team: object.result.my_module.team)
|
||||||
|
end
|
||||||
|
|
||||||
|
def text
|
||||||
|
sanitize_input(object.tinymce_render('text'))
|
||||||
|
end
|
||||||
|
|
||||||
|
def icon
|
||||||
|
'fa-font'
|
||||||
|
end
|
||||||
|
|
||||||
|
def urls
|
||||||
|
result = object.result
|
||||||
|
|
||||||
|
return {} if object.destroyed? || !can_manage_result?(scope[:user] || @instance_options[:user], result)
|
||||||
|
|
||||||
|
{
|
||||||
|
duplicate_url: duplicate_my_module_result_text_path(result.my_module, result, object),
|
||||||
|
delete_url: my_module_result_text_path(result.my_module, result, object),
|
||||||
|
update_url: my_module_result_text_path(result.my_module, result, object)
|
||||||
|
}
|
||||||
|
end
|
||||||
|
end
|
|
@ -84,7 +84,7 @@ class StepSerializer < ActiveModel::Serializer
|
||||||
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),
|
||||||
update_asset_view_mode_url: update_asset_view_mode_step_path(object),
|
update_asset_view_mode_url: update_asset_view_mode_step_path(object),
|
||||||
update_view_state_step_url: update_view_state_step_path(object),
|
update_view_state_url: update_view_state_step_path(object),
|
||||||
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)
|
||||||
|
|
|
@ -18,4 +18,8 @@
|
||||||
<results url="<%= my_module_results_url(@my_module) %>">
|
<results url="<%= my_module_results_url(@my_module) %>">
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<%= javascript_include_tag "handsontable.full" %>
|
||||||
|
<%= render partial: "shared/formulas_libraries" %>
|
||||||
|
<%= render 'shared/tiny_mce_packs' %>
|
||||||
<%= javascript_include_tag 'vue_results' %>
|
<%= javascript_include_tag 'vue_results' %>
|
||||||
|
|
||||||
|
|
|
@ -535,8 +535,21 @@ Rails.application.routes.draw do
|
||||||
get 'users/edit', to: 'user_my_modules#index_edit'
|
get 'users/edit', to: 'user_my_modules#index_edit'
|
||||||
|
|
||||||
resources :results, only: %i(index show create update destroy) do
|
resources :results, only: %i(index show create update destroy) do
|
||||||
get :elements
|
member do
|
||||||
get :assets
|
get :elements
|
||||||
|
get :assets
|
||||||
|
end
|
||||||
|
|
||||||
|
resources :tables, controller: 'result_elements/tables', only: %i(create destroy update) do
|
||||||
|
member do
|
||||||
|
post :duplicate
|
||||||
|
end
|
||||||
|
end
|
||||||
|
resources :texts, controller: 'result_elements/texts', only: %i(create destroy update) do
|
||||||
|
member do
|
||||||
|
post :duplicate
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue