Merge pull request #4215 from aignatov-bio/ai-sci-6939-fix-smart-annotation-in-new-steps

Fix smart annotation for step editing [SCI-6939]
This commit is contained in:
aignatov-bio 2022-07-11 10:38:42 +02:00 committed by GitHub
commit fa623a9f19
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 73 additions and 48 deletions

View file

@ -5,28 +5,21 @@
position: relative; position: relative;
width: calc(100% + 16px); width: calc(100% + 16px);
.action-container {
cursor: pointer;
height: 100%;
left: 0;
position: absolute;
width: 100%;
z-index: 100;
.buttons-container { .buttons-container {
background: linear-gradient( background: linear-gradient(
90deg, 90deg,
transparent 0%, transparent 0%,
$color-concrete 25%, $color-concrete 25%,
$color-concrete 100% $color-concrete 100%
); );
display: none; display: none;
padding-left: 2em; padding-left: 2em;
position: absolute; position: absolute;
right: 0; right: 0;
}
} }
.element-grip { .element-grip {
align-items: center; align-items: center;
color: $color-silver-chalice; color: $color-silver-chalice;
@ -66,7 +59,8 @@
} }
&.edit { &.edit {
.action-container { .buttons-container,
.element-grip {
display: none; display: none;
} }
} }

View file

@ -298,14 +298,14 @@
let index = this.elements.findIndex((e) => e.id === element.id); let index = this.elements.findIndex((e) => e.id === element.id);
if (skipRequest) { if (skipRequest) {
this.elements[index].orderable = element; this.elements[index].attributes.orderable = element.attributes.orderable;
} else { } else {
$.ajax({ $.ajax({
url: element.attributes.orderable.urls.update_url, url: element.attributes.orderable.urls.update_url,
method: 'PUT', method: 'PUT',
data: element.attributes.orderable, data: element.attributes.orderable,
success: (result) => { success: (result) => {
this.elements[index].orderable = result; this.elements[index].attributes.orderable = result.data.attributes;
} }
}).error(() => { }).error(() => {
HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger'); HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger');

View file

@ -8,10 +8,12 @@
<InlineEdit <InlineEdit
v-if="element.attributes.orderable.urls.update_url" v-if="element.attributes.orderable.urls.update_url"
:value="element.attributes.orderable.name" :value="element.attributes.orderable.name"
:sa_value="element.attributes.orderable.sa_name"
:characterLimit="255" :characterLimit="255"
:placeholder="''" :placeholder="''"
:allowBlank="false" :allowBlank="false"
:autofocus="editingName" :autofocus="editingName"
:smartAnnotation="true"
:attributeName="`${i18n.t('Checklist')} ${i18n.t('name')}`" :attributeName="`${i18n.t('Checklist')} ${i18n.t('name')}`"
@editingEnabled="editingName = true" @editingEnabled="editingName = true"
@editingDisabled="editingName = false" @editingDisabled="editingName = false"
@ -149,13 +151,16 @@
}, },
saveItem(item) { saveItem(item) {
if (item.attributes.id) { if (item.attributes.id) {
this.checklistItems.splice(
item.attributes.position, 1, item
);
$.ajax({ $.ajax({
url: item.attributes.urls.update_url, url: item.attributes.urls.update_url,
type: 'PATCH', type: 'PATCH',
data: item, data: item,
success: (result) => {
let updatedItem = this.checklistItems[item.attributes.position]
updatedItem.attributes = result.data.attributes
updatedItem.attributes.id = item.attributes.id
this.$set(this.checklistItems, item.attributes.position, updatedItem)
},
error: () => HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger') error: () => HelperModule.flashAlertMsg(this.i18n.t('errors.general'), 'danger')
}); });
} else { } else {

View file

@ -17,12 +17,14 @@
<InlineEdit <InlineEdit
v-if="!checklistItem.attributes.urls || updateUrl" v-if="!checklistItem.attributes.urls || updateUrl"
:value="checklistItem.attributes.text" :value="checklistItem.attributes.text"
:sa_value="checklistItem.attributes.sa_text"
:characterLimit="10000" :characterLimit="10000"
:placeholder="''" :placeholder="''"
:allowBlank="true" :allowBlank="true"
:autofocus="editingText" :autofocus="editingText"
:attributeName="`${i18n.t('ChecklistItem')} ${i18n.t('name')}`" :attributeName="`${i18n.t('ChecklistItem')} ${i18n.t('name')}`"
:multilinePaste="true" :multilinePaste="true"
:smartAnnotation="true"
@editingEnabled="enableTextEdit" @editingEnabled="enableTextEdit"
@editingDisabled="disableTextEdit" @editingDisabled="disableTextEdit"
@update="updateText" @update="updateText"

View file

@ -1,21 +1,18 @@
<template> <template>
<div class="step-text-container" :class="{ 'edit': inEditMode }" @keyup.enter="enableEditMode($event)" tabindex="0"> <div class="step-text-container" :class="{ 'edit': inEditMode }" @keyup.enter="enableEditMode($event)" tabindex="0">
<div ref="actionContainer" class="action-container" @click="enableEditMode($event)"> <div v-if="reorderElementUrl" 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 v-if="element.attributes.orderable.urls.update_url" class="btn icon-btn btn-light" tabindex="-1" @click="enableEditMode($event)">
<button v-if="element.attributes.orderable.urls.update_url" class="btn icon-btn btn-light" tabindex="-1"> <i class="fas fa-pen"></i>
<i class="fas fa-pen"></i> </button>
</button> <button v-if="element.attributes.orderable.urls.delete_url" class="btn icon-btn btn-light" @click="showDeleteModal" tabindex="-1">
<button v-if="element.attributes.orderable.urls.delete_url" class="btn icon-btn btn-light" @click="showDeleteModal" tabindex="-1"> <i class="fas fa-trash"></i>
<i class="fas fa-trash"></i> </button>
</button>
</div>
</div> </div>
<Tinymce <Tinymce
v-if="element.attributes.orderable.urls.update_url" v-if="element.attributes.orderable.urls.update_url"
: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"
:placeholder="i18n.t('protocols.steps.text.placeholder')" :placeholder="i18n.t('protocols.steps.text.placeholder')"
@ -26,6 +23,7 @@
:lastUpdated="element.attributes.orderable.updated_at" :lastUpdated="element.attributes.orderable.updated_at"
@update="update" @update="update"
@editingDisabled="disableEditMode" @editingDisabled="disableEditMode"
@editingEnabled="enableEditMode"
/> />
<div v-else v-html="element.attributes.orderable.text_view"></div> <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"/>
@ -70,10 +68,6 @@
}, },
methods: { methods: {
enableEditMode() { enableEditMode() {
if (
$(this.$refs.actionContainer).hasClass('fas fa-grip-vertical') ||
$(this.$refs.actionContainer).hasClass('element-grip')
) return
if (!this.element.attributes.orderable.urls.update_url) return if (!this.element.attributes.orderable.urls.update_url) return
if (this.inEditMode == true) return if (this.inEditMode == true) return
this.inEditMode = true this.inEditMode = true

View file

@ -1,5 +1,5 @@
<template> <template>
<div class="sci-inline-edit" :class="{ 'editing': editing }" tabindex="0" @keyup.enter="enableEdit"> <div class="sci-inline-edit" :class="{ 'editing': editing }" tabindex="0" @keyup.enter="enableEdit($event)">
<div class="sci-inline-edit__content"> <div class="sci-inline-edit__content">
<textarea <textarea
ref="input" ref="input"
@ -13,7 +13,7 @@
@paste="handlePaste" @paste="handlePaste"
@blur="handleBlur" @blur="handleBlur"
></textarea> ></textarea>
<div v-else @click="enableEdit" class="sci-inline-edit__view" :class="{ 'blank': isBlank }">{{ value || placeholder }}</div> <div v-else @click="enableEdit($event)" class="sci-inline-edit__view" v-html="sa_value || value || placeholder" :class="{ 'blank': isBlank }"></div>
<div v-if="editing && error" class="sci-inline-edit__error"> <div v-if="editing && error" class="sci-inline-edit__error">
{{ error }} {{ error }}
</div> </div>
@ -34,12 +34,14 @@
name: 'InlineEdit', name: 'InlineEdit',
props: { props: {
value: { type: String, default: '' }, value: { type: String, default: '' },
sa_value: { type: String},
allowBlank: { type: Boolean, default: true }, allowBlank: { type: Boolean, default: true },
attributeName: { type: String, required: true }, attributeName: { type: String, required: true },
characterLimit: { type: Number }, characterLimit: { type: Number },
placeholder: { type: String }, placeholder: { type: String },
autofocus: { type: Boolean, default: false }, autofocus: { type: Boolean, default: false },
multilinePaste: { type: Boolean, default: false }, multilinePaste: { type: Boolean, default: false },
smartAnnotation: { type: Boolean, default: false },
editOnload: { type: Boolean, default: false } editOnload: { type: Boolean, default: false }
}, },
data() { data() {
@ -89,6 +91,8 @@
} }
}, },
handleBlur() { handleBlur() {
if ($('.atwho-view:visible').length) return;
if (this.allowBlank || !this.isBlank) { if (this.allowBlank || !this.isBlank) {
this.$nextTick(this.update); this.$nextTick(this.update);
} else { } else {
@ -103,9 +107,15 @@
this.resize(); this.resize();
}); });
}, },
enableEdit() { enableEdit(e) {
if (e && $(e.target).hasClass('atwho-user-popover')) return
this.editing = true; this.editing = true;
this.focus(); this.focus();
this.$nextTick(() => {
if (this.smartAnnotation) {
SmartAnnotation.init($(this.$refs.input));
}
})
this.$emit('editingEnabled'); this.$emit('editingEnabled');
}, },
cancelEdit() { cancelEdit() {
@ -145,7 +155,7 @@
setTimeout(() => { setTimeout(() => {
if(!this.allowBlank && this.isBlank) return; if(!this.allowBlank && this.isBlank) return;
if(!this.editing) return; if(!this.editing) return;
this.newValue = this.$refs.input.value // Fix for smart annotation
this.newValue = this.newValue.trim(); this.newValue = this.newValue.trim();
this.editing = false; this.editing = false;
this.$emit('editingDisabled'); this.$emit('editingDisabled');

View file

@ -3,8 +3,18 @@
class ChecklistItemSerializer < ActiveModel::Serializer class ChecklistItemSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
include ApplicationHelper
include ActionView::Helpers::TextHelper
attributes :id, :text, :checked, :position, :urls attributes :id, :text, :checked, :position, :urls, :sa_text
def sa_text
@user = scope[:user] || @instance_options[:user]
custom_auto_link(object.text,
simple_format: false,
tags: %w(img),
team: object.checklist.step.protocol.team)
end
def urls def urls
return {} if object.destroyed? || return {} if object.destroyed? ||

View file

@ -3,14 +3,24 @@
class ChecklistSerializer < ActiveModel::Serializer class ChecklistSerializer < ActiveModel::Serializer
include Canaid::Helpers::PermissionsHelper include Canaid::Helpers::PermissionsHelper
include Rails.application.routes.url_helpers include Rails.application.routes.url_helpers
include ApplicationHelper
include ActionView::Helpers::TextHelper
attributes :id, :name, :urls, :icon attributes :id, :name, :urls, :icon, :sa_name
has_many :checklist_items, serializer: ChecklistItemSerializer has_many :checklist_items, serializer: ChecklistItemSerializer
def icon def icon
'fa-list-ul' 'fa-list-ul'
end end
def sa_name
@user = scope[:user] || @instance_options[:user]
custom_auto_link(object.name,
simple_format: false,
tags: %w(img),
team: object.step.protocol.team)
end
def urls def urls
return {} if object.destroyed? || !can_manage_step?(scope[:user] || @instance_options[:user], object.step) return {} if object.destroyed? || !can_manage_step?(scope[:user] || @instance_options[:user], object.step)