Add field duplicate option [SCI-11345]

This commit is contained in:
Anton 2025-03-04 13:48:24 +01:00
parent 9d4141eb13
commit b045b175c7
9 changed files with 71 additions and 4 deletions

View file

@ -3,8 +3,8 @@
class FormFieldsController < ApplicationController
before_action :check_forms_enabled
before_action :load_form
before_action :load_form_field, only: %i(update destroy)
before_action :check_manage_permissions, only: %i(create update destroy reorder)
before_action :load_form_field, only: %i(update destroy duplicate)
before_action :check_manage_permissions, only: %i(create update destroy reorder duplicate)
def create
ActiveRecord::Base.transaction do
@ -39,6 +39,21 @@ class FormFieldsController < ApplicationController
end
end
def duplicate
ActiveRecord::Base.transaction do
new_form_field = @form_field.duplicate!(current_user)
log_activity(:form_block_duplicated, block_name: @form_field.name)
render json: new_form_field, serializer: FormFieldSerializer, user: current_user
rescue ActiveRecord::RecordInvalid => e
render json: { error: e.errors.full_messages }, status: :unprocessable_entity
raise ActiveRecord::Rollback
rescue StandardError => e
render json: { message: I18n.t('errors.general') }, status: :unprocessable_entity
Rails.logger.error e.message
raise ActiveRecord::Rollback
end
end
def destroy
ActiveRecord::Base.transaction do
if @form_field.destroy

View file

@ -28,6 +28,9 @@
</button>
</template>
<template v-slot:flyout>
<div @click="duplicateField" class="py-2.5 px-3 hover:bg-sn-super-light-grey cursor-pointer ">
{{ i18n.t('forms.show.duplicate') }}
</div>
<div @click="deleteField" class="py-2.5 px-3 hover:bg-sn-super-light-grey cursor-pointer text-sn-delete-red">
{{ i18n.t('forms.show.delete') }}
</div>
@ -146,6 +149,9 @@ export default {
deleteField() {
this.$emit('delete', this.editField);
},
duplicateField() {
this.$emit('duplicate', this.editField);
},
syncField(field) {
this.editField = field;
}

View file

@ -102,6 +102,7 @@
:icon="fieldIcon[activeField.attributes.data.type]"
@update="updateField"
@delete="deleteField"
@duplicate="duplicateField"
/>
<div v-if="!activeField.id"
class="text-xl font-semibold text-sn-grey font-inter flex items-center justify-center w-full h-full">
@ -243,6 +244,23 @@ export default {
this.syncSavedFields();
});
},
duplicateField(field) {
if (this.submitting) {
return;
}
this.submitting = true;
axios.post(field.attributes.urls.duplicate, {
form_field_id: field.id
}).then((response) => {
const index = this.fields.findIndex((f) => f.id === field.id);
this.fields.splice(index + 1, 0, response.data.data);
this.activeField = response.data.data;
this.syncSavedFields();
}).finally(() => {
this.submitting = false;
});
},
deleteField(field) {
if (this.submitting) {
return;

View file

@ -1,6 +1,8 @@
# frozen_string_literal: true
class FormField < ApplicationRecord
include Cloneable
belongs_to :form
belongs_to :created_by, class_name: 'User'
belongs_to :last_modified_by, class_name: 'User'
@ -11,4 +13,22 @@ class FormField < ApplicationRecord
validates :position, presence: true, uniqueness: { scope: :form }
acts_as_list scope: :form, top_of_list: 0, sequential_updates: true
def parent
form
end
def duplicate!(user = nil)
new_form_field = dup
new_form_field.name = next_clone_name
new_form_field.created_by = user || created_by
new_form_field.position = form.form_fields.size
new_form_field.save!
new_form_field.with_lock do
new_form_field.insert_at(position + 1)
end
new_form_field
end
end

View file

@ -13,7 +13,8 @@ class FormFieldSerializer < ActiveModel::Serializer
def urls
{
show: form_form_field_path(object.form, object)
show: form_form_field_path(object.form, object),
duplicate: duplicate_form_form_field_path(object.form, object)
}
end
end

View file

@ -554,6 +554,7 @@ class Extends
protocol_step_form_added: 353,
protocol_step_form_deleted: 354,
protocol_step_form_moved: 355,
form_block_duplicated: 356,
form_duplicated: 357
}
@ -578,7 +579,7 @@ class Extends
storage_locations: [*309..315],
container_storage_locations: [*316..322, 326],
storage_location_repository_rows: [*323..325],
forms: [331, 332, 333, 334, 335, 336, *337..346, 357]
forms: [331, 332, 333, 334, 335, 336, *337..346, 356, 357]
}
TOP_LEVEL_ASSIGNABLES = %w(Project Team Protocol Repository Form).freeze

View file

@ -1124,6 +1124,7 @@ en:
mark_as_na: 'Mark as N/A'
mark_as_na_explanation: 'Allow user to mark the field as Not-applicable.'
delete: 'Delete'
duplicate: 'Duplicate'
edit_form: 'Edit form'
test_form: 'Test form'
publish: 'Publish'

View file

@ -370,6 +370,7 @@ en:
protocol_step_form_deleted_html: "%{user} deleted form <strong>%{form}</strong> in protocol's step %{step_position} <strong>%{step}</strong> in Protocol repository."
protocol_step_form_moved_html: "%{user} moved form <strong>%{form}</strong> in protocol's step %{step_position} <strong>%{step}</strong> to step %{step_position_destination} <strong>%{step_destination}</strong> in Protocol repository."
form_duplicated_html: "%{user} duplicated form %{form_new} from %{form} in Forms."
form_block_duplicated_html: "%{user} duplicated form block %{block_name} in form %{form} in Form templates."
activity_name:
create_project: "Project created"
rename_project: "Project renamed"
@ -697,6 +698,7 @@ en:
protocol_step_form_deleted: "Task step form deleted"
protocol_step_form_moved: "Task step form moved"
form_duplciated: "Form duplicated"
form_block_duplicated: "Form block duplicated"
activity_group:
projects: "Projects"
task_results: "Task results"

View file

@ -881,6 +881,9 @@ Rails.application.routes.draw do
end
resources :form_fields, only: %i(create update destroy) do
member do
post :duplicate
end
collection do
post :reorder
end