diff --git a/app/controllers/form_fields_controller.rb b/app/controllers/form_fields_controller.rb
index 8adb1fa28..d637710e3 100644
--- a/app/controllers/form_fields_controller.rb
+++ b/app/controllers/form_fields_controller.rb
@@ -39,7 +39,7 @@ class FormFieldsController < ApplicationController
if @form_field.discard
render json: {}
else
- render json: { error: @storage_location.errors.full_messages }, status: :unprocessable_entity
+ render json: { error: @form_field.errors.full_messages }, status: :unprocessable_entity
end
end
end
@@ -47,8 +47,8 @@ class FormFieldsController < ApplicationController
def reorder
ActiveRecord::Base.transaction do
params.permit(form_field_positions: %i(position id))[:form_field_positions].each do |data|
- form_field = @form.form_fields.find(data['id'].to_i)
- form_field.insert_at(data['position'].to_i)
+ form_field = @form.form_fields.find(data[:id])
+ form_field.insert_at(data[:position].to_i)
end
end
diff --git a/app/controllers/forms_controller.rb b/app/controllers/forms_controller.rb
index b1c8961bb..577d52ca2 100644
--- a/app/controllers/forms_controller.rb
+++ b/app/controllers/forms_controller.rb
@@ -2,6 +2,7 @@
class FormsController < ApplicationController
before_action :load_form, only: %i(show update publish unpublish)
+ before_action :set_breadcrumbs_items, only: %i(index show)
def index
respond_to do |format|
@@ -10,14 +11,15 @@ class FormsController < ApplicationController
forms = Lists::FormsService.new(current_user, current_team, params).call
render json: forms,
each_serializer: Lists::FormSerializer,
- user: current_user
+ user: current_user,
+ meta: pagination_dict(forms)
end
end
end
def show
respond_to do |format|
- format.json { render json: @form, serializer: Lists::FormSerializer, include: %i(form_fields), user: current_user }
+ format.json { render json: @form, serializer: FormSerializer, include: %i(form_fields), user: current_user }
format.html
end
end
@@ -117,8 +119,36 @@ class FormsController < ApplicationController
end
end
+ def actions_toolbar
+ render json: {
+ actions:
+ Toolbars::FormsService.new(
+ current_user,
+ form_ids: JSON.parse(params[:items]).map { |i| i['id'] }
+ ).actions
+ }
+ end
+
private
+ def set_breadcrumbs_items
+ @breadcrumbs_items = []
+
+ @breadcrumbs_items.push(
+ { label: t('breadcrumbs.templates') }
+ )
+
+ @breadcrumbs_items.push(
+ { label: t('breadcrumbs.forms'), url: forms_path }
+ )
+
+ if @form
+ @breadcrumbs_items.push(
+ { label: @form.name }
+ )
+ end
+ end
+
def load_form
@form = Form.find_by(id: params[:id])
diff --git a/app/helpers/left_menu_bar_helper.rb b/app/helpers/left_menu_bar_helper.rb
index b6a2d22d2..60929d6d6 100644
--- a/app/helpers/left_menu_bar_helper.rb
+++ b/app/helpers/left_menu_bar_helper.rb
@@ -35,6 +35,10 @@ module LeftMenuBarHelper
icon: 'sn-icon-protocols-templates',
active: protocols_are_selected? || label_templates_are_selected?,
submenu: [{
+ url: forms_path,
+ name: t('left_menu_bar.forms'),
+ active: forms_are_selected?
+ }, {
url: protocols_path,
name: t('left_menu_bar.protocol'),
active: protocols_are_selected?
@@ -79,6 +83,10 @@ module LeftMenuBarHelper
controller_name == 'protocols'
end
+ def forms_are_selected?
+ controller_name == 'forms'
+ end
+
def label_templates_are_selected?
controller_name == 'label_templates'
end
diff --git a/app/javascript/packs/vue/forms_show.js b/app/javascript/packs/vue/forms_show.js
new file mode 100644
index 000000000..6391a90c9
--- /dev/null
+++ b/app/javascript/packs/vue/forms_show.js
@@ -0,0 +1,10 @@
+import { createApp } from 'vue/dist/vue.esm-bundler.js';
+import PerfectScrollbar from 'vue3-perfect-scrollbar';
+import FormShow from '../../vue/forms/show.vue';
+import { mountWithTurbolinks } from './helpers/turbolinks.js';
+
+const app = createApp();
+app.component('FormShow', FormShow);
+app.config.globalProperties.i18n = window.I18n;
+app.use(PerfectScrollbar);
+mountWithTurbolinks(app, '#formShow');
diff --git a/app/javascript/packs/vue/forms_table.js b/app/javascript/packs/vue/forms_table.js
new file mode 100644
index 000000000..26de6a572
--- /dev/null
+++ b/app/javascript/packs/vue/forms_table.js
@@ -0,0 +1,10 @@
+import { createApp } from 'vue/dist/vue.esm-bundler.js';
+import PerfectScrollbar from 'vue3-perfect-scrollbar';
+import FormsTable from '../../vue/forms/table.vue';
+import { mountWithTurbolinks } from './helpers/turbolinks.js';
+
+const app = createApp();
+app.component('FormsTable', FormsTable);
+app.config.globalProperties.i18n = window.I18n;
+app.use(PerfectScrollbar);
+mountWithTurbolinks(app, '#formsTable');
diff --git a/app/javascript/vue/forms/edit_field.vue b/app/javascript/vue/forms/edit_field.vue
new file mode 100644
index 000000000..fe69c678c
--- /dev/null
+++ b/app/javascript/vue/forms/edit_field.vue
@@ -0,0 +1,95 @@
+
+
+
+
{{ i18n.t(`forms.show.blocks.${editField.attributes.type}`) }}
+
+
+
+
+
+
+ {{ i18n.t('forms.show.required_label') }}
+
+
+
+
+
+
+
+ {{ i18n.t('forms.show.delete') }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
{{ i18n.t('forms.show.mark_as_na') }}
+
+
+
+
+
+
{{ i18n.t('forms.show.mark_as_na_explanation') }}
+
+
+
+
+
diff --git a/app/javascript/vue/forms/renderers/name.vue b/app/javascript/vue/forms/renderers/name.vue
new file mode 100644
index 000000000..ac969e84f
--- /dev/null
+++ b/app/javascript/vue/forms/renderers/name.vue
@@ -0,0 +1,16 @@
+
+
+ {{ params.data.name }}
+
+
+
+
diff --git a/app/javascript/vue/forms/show.vue b/app/javascript/vue/forms/show.vue
new file mode 100644
index 000000000..5ab29a66a
--- /dev/null
+++ b/app/javascript/vue/forms/show.vue
@@ -0,0 +1,169 @@
+
+
+
+
+
+
+
{{ i18n.t('forms.show.build_form') }}
+
+
+ {{ field.attributes.name }}
+
+
+
+
+
+
+
+
+ {{ e.name }}
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/javascript/vue/forms/table.vue b/app/javascript/vue/forms/table.vue
new file mode 100644
index 000000000..c5694de0f
--- /dev/null
+++ b/app/javascript/vue/forms/table.vue
@@ -0,0 +1,113 @@
+
+
+
+
+
+
+
diff --git a/app/models/form.rb b/app/models/form.rb
index ef0597540..3ea72afeb 100644
--- a/app/models/form.rb
+++ b/app/models/form.rb
@@ -1,6 +1,8 @@
# frozen_string_literal: true
class Form < ApplicationRecord
+ ID_PREFIX = 'FR'
+ include PrefixedIdModel
include ArchivableModel
belongs_to :team
diff --git a/app/models/form_field.rb b/app/models/form_field.rb
index f4f432a45..30d97f576 100644
--- a/app/models/form_field.rb
+++ b/app/models/form_field.rb
@@ -3,6 +3,8 @@
class FormField < ApplicationRecord
include Discard::Model
+ default_scope -> { kept }
+
belongs_to :form
belongs_to :created_by, class_name: 'User'
belongs_to :last_modified_by, class_name: 'User'
@@ -11,5 +13,8 @@ class FormField < ApplicationRecord
validates :description, length: { maximum: Constants::NAME_MAX_LENGTH }
validates :position, presence: true, uniqueness: { scope: :form }
- acts_as_list scope: :form, top_of_list: 0, sequential_updates: true
+ acts_as_list scope: [:form, discarded_at: nil], top_of_list: 0, sequential_updates: true
+
+ private
+
end
diff --git a/app/serializers/form_field_serializer.rb b/app/serializers/form_field_serializer.rb
index 30b8d1f8d..a9a1f001f 100644
--- a/app/serializers/form_field_serializer.rb
+++ b/app/serializers/form_field_serializer.rb
@@ -1,9 +1,19 @@
# frozen_string_literal: true
class FormFieldSerializer < ActiveModel::Serializer
- attributes :id, :name, :description, :updated_at, :type, :required, :allow_not_applicable, :uid, :position
+ include Canaid::Helpers::PermissionsHelper
+ include Rails.application.routes.url_helpers
+
+ attributes :id, :name, :description, :updated_at, :type, :required,
+ :allow_not_applicable, :uid, :position, :urls
def type
- object.data[:type]
+ object.data['type']
+ end
+
+ def urls
+ {
+ show: form_form_field_path(object.form, object)
+ }
end
end
diff --git a/app/serializers/form_serializer.rb b/app/serializers/form_serializer.rb
new file mode 100644
index 000000000..ee2c4ee9c
--- /dev/null
+++ b/app/serializers/form_serializer.rb
@@ -0,0 +1,33 @@
+# frozen_string_literal: true
+
+class FormSerializer < ActiveModel::Serializer
+ include Canaid::Helpers::PermissionsHelper
+ include Rails.application.routes.url_helpers
+
+ attributes :id, :name, :published_on, :published_by, :updated_at, :urls
+
+ has_many :form_fields,
+ key: :form_fields,
+ serializer: FormFieldSerializer,
+ order: :position
+
+
+ def published_by
+ object.published_by&.full_name
+ end
+
+ def published_on
+ I18n.l(object.published_on, format: :full) if object.published_on
+ end
+
+ def updated_at
+ I18n.l(object.updated_at, format: :full) if object.updated_at
+ end
+
+ def urls
+ {
+ show: form_path(object),
+ create_field: form_form_fields_path(object)
+ }
+ end
+end
diff --git a/app/serializers/lists/form_serializer.rb b/app/serializers/lists/form_serializer.rb
index a44bce9bd..fc2a21c1e 100644
--- a/app/serializers/lists/form_serializer.rb
+++ b/app/serializers/lists/form_serializer.rb
@@ -2,9 +2,10 @@
module Lists
class FormSerializer < ActiveModel::Serializer
- attributes :id, :name, :description, :published_on, :published_by, :updated_at
+ include Canaid::Helpers::PermissionsHelper
+ include Rails.application.routes.url_helpers
- has_many :form_fields, key: :form_fields, serializer: FormFieldSerializer
+ attributes :id, :name, :published_on, :published_by, :updated_at, :urls, :code
def published_by
object.published_by&.full_name
@@ -17,5 +18,11 @@ module Lists
def updated_at
I18n.l(object.updated_at, format: :full) if object.updated_at
end
+
+ def urls
+ {
+ show: form_path(object)
+ }
+ end
end
end
diff --git a/app/services/toolbars/forms_service.rb b/app/services/toolbars/forms_service.rb
new file mode 100644
index 000000000..7885f9ebb
--- /dev/null
+++ b/app/services/toolbars/forms_service.rb
@@ -0,0 +1,26 @@
+# frozen_string_literal: true
+
+module Toolbars
+ class FormsService
+ attr_reader :current_user
+
+ include Canaid::Helpers::PermissionsHelper
+ include Rails.application.routes.url_helpers
+
+ def initialize(current_user, form_ids: [])
+ @current_user = current_user
+ @forms = Form.where(id: forms_ids)
+
+ @single = @forms.length == 1
+ end
+
+ def actions
+ return [] if @forms.none?
+
+ [].compact
+ end
+
+ private
+
+ end
+end
diff --git a/app/views/forms/index.html.erb b/app/views/forms/index.html.erb
new file mode 100644
index 000000000..4c7eaa5a2
--- /dev/null
+++ b/app/views/forms/index.html.erb
@@ -0,0 +1,18 @@
+
+<%= javascript_include_tag 'vue_form_table' %>
diff --git a/app/views/forms/show.html.erb b/app/views/forms/show.html.erb
new file mode 100644
index 000000000..6df8ef32d
--- /dev/null
+++ b/app/views/forms/show.html.erb
@@ -0,0 +1,7 @@
+
+
+
+
+<%= javascript_include_tag 'vue_form_show' %>
diff --git a/config/initializers/extends.rb b/config/initializers/extends.rb
index 8f8854f46..09e1b621f 100644
--- a/config/initializers/extends.rb
+++ b/config/initializers/extends.rb
@@ -678,6 +678,7 @@ class Extends
search/index
storage_locations/index
storage_locations/show
+ forms/show
)
DEFAULT_USER_NOTIFICATION_SETTINGS = {
@@ -706,6 +707,8 @@ class Extends
ProjectList_archived_state
ProtocolTemplates_active_state
ProtocolTemplates_archived_state
+ FormsTable_active_state
+ FormsTable_archived_state
ReportTemplates_active_state
ReportTemplates_archived_state
Repositories_active_state
diff --git a/config/locales/en.yml b/config/locales/en.yml
index d900109db..8fae6c7f9 100644
--- a/config/locales/en.yml
+++ b/config/locales/en.yml
@@ -350,6 +350,7 @@ en:
projects: "Projects"
repositories: "Inventories"
templates: "Templates"
+ forms: "Forms"
protocol: "Protocol"
label: "Label"
items: "Items"
@@ -1053,7 +1054,6 @@ en:
select_user_role: "Please select a user role."
add_user_generic_error: "An error occurred. "
can_add_user_to_project: "Can not add user to the project."
-
forms:
default_name: "Untitled form"
restored:
@@ -1062,7 +1062,38 @@ en:
archived:
success_flash: "%{number} form(s) successfully archived."
error_flash: "Failed to archive form(s)."
-
+ index:
+ head_title: "Forms"
+ toolbar:
+ new: 'New form'
+ table:
+ name: 'Form name'
+ code: 'ID'
+ versions: 'Versions'
+ used_in_protocols: 'Used in protocols'
+ access: 'Access'
+ published_by: 'Published by'
+ published_on: 'Published on'
+ updated_on: 'Updated on'
+ show:
+ build_form: 'Build your form'
+ add_block: 'Add a block'
+ title_label: Title (required)
+ title_placeholder: 'Add a title'
+ description_label: Description
+ description_placeholder: 'Add a description'
+ required_label: 'Required'
+ mark_as_na: 'Mark as N/A'
+ mark_as_na_explanation: 'Allow user to mark the field as Not-applicable.'
+ delete: 'Delete'
+ test_form: 'Test form'
+ publish: 'Publish'
+ blocks:
+ text: 'Text'
+ number: 'Number'
+ single_choice: 'Single choice'
+ multiple_choice: 'Multiple choice'
+ datetime: 'Date & Time'
label_templates:
types:
fluics_label_template: 'Fluics'
@@ -4483,6 +4514,7 @@ en:
locations: "Locations"
label_printer: "Label printer"
fluics_printer: "Fluics printer"
+ forms: "Forms"
Add: "Add"
Asset: "File"
diff --git a/config/routes.rb b/config/routes.rb
index 2bca68964..de8727242 100644
--- a/config/routes.rb
+++ b/config/routes.rb
@@ -857,6 +857,7 @@ Rails.application.routes.draw do
end
collection do
+ get :actions_toolbar
post :archive
post :restore
end
diff --git a/config/webpack/webpack.config.js b/config/webpack/webpack.config.js
index 07ac593cd..0a8e3d022 100644
--- a/config/webpack/webpack.config.js
+++ b/config/webpack/webpack.config.js
@@ -68,7 +68,9 @@ const entryList = {
vue_legacy_repository_menu_dropdown: './app/javascript/packs/vue/legacy/repository_menu_dropdown.js',
vue_dashboard_new_task: './app/javascript/packs/vue/dashboard_new_task.js',
vue_storage_locations_table: './app/javascript/packs/vue/storage_locations_table.js',
- vue_storage_locations_container: './app/javascript/packs/vue/storage_locations_container.js'
+ vue_storage_locations_container: './app/javascript/packs/vue/storage_locations_container.js',
+ vue_form_show: './app/javascript/packs/vue/forms_show.js',
+ vue_form_table: './app/javascript/packs/vue/forms_table.js'
};
// Engine pack loading based on https://github.com/rails/webpacker/issues/348#issuecomment-635480949
diff --git a/db/migrate/20241209074134_create_forms.rb b/db/migrate/20241209074134_create_forms.rb
index 3f38ef60f..73c0abf01 100644
--- a/db/migrate/20241209074134_create_forms.rb
+++ b/db/migrate/20241209074134_create_forms.rb
@@ -33,8 +33,6 @@ class CreateForms < ActiveRecord::Migration[7.0]
t.string :uid
t.datetime :discarded_at, index: true
- t.index %i(form_id position), unique: true
-
t.timestamps
end
end
diff --git a/spec/controllers/form_fields_controller_spec.rb b/spec/controllers/form_fields_controller_spec.rb
index e0cb0b99c..3e6739704 100644
--- a/spec/controllers/form_fields_controller_spec.rb
+++ b/spec/controllers/form_fields_controller_spec.rb
@@ -47,7 +47,7 @@ describe FormFieldsController, type: :controller do
end
end
- describe 'PUT create' do
+ describe 'PUT update' do
let(:action) { put :update, params: params, format: :json }
let(:params) do
{