You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
10 KiB
290 lines
10 KiB
# frozen_string_literal: true
|
|
|
|
require_relative '../css_classes_manager'
|
|
|
|
module QuestionsCrafter
|
|
module Ui
|
|
module Components
|
|
class FormBuilder < ::Phlex::HTML
|
|
include Phlex::Rails::Helpers::FormWith
|
|
include Phlex::Rails::Helpers::Checkbox
|
|
include ActionView::Helpers::TextHelper # For pluralize
|
|
|
|
def initialize(questions_object:, form_url:, form_method:, read_only:, render_top_error_bar: false, custom_html_classes: {})
|
|
@questions_object = questions_object
|
|
@class_prefix = QuestionsCrafter::Utils.html_class_name_prefix(questions: questions_object)
|
|
@errors = nil
|
|
if @questions_object.changed? && @questions_object.invalid?
|
|
# TODO: The changed? method is not a strong one.. check if there's a better way
|
|
@errors = @questions_object.errors
|
|
end
|
|
@form_url = form_url
|
|
@form_method = form_method
|
|
@read_only = read_only
|
|
@render_top_error_bar = render_top_error_bar
|
|
@css_manager = CssClassesManager.new(custom_html_classes)
|
|
end
|
|
|
|
def errors
|
|
@errors || []
|
|
end
|
|
|
|
def read_only
|
|
@read_only
|
|
end
|
|
|
|
def questions_object
|
|
@questions_object
|
|
end
|
|
|
|
def render_top_error_bar
|
|
@render_top_error_bar
|
|
end
|
|
|
|
def css_classes(section, key = nil)
|
|
@css_manager.get(section, key)
|
|
end
|
|
|
|
private
|
|
|
|
def required_fields_message_notice
|
|
div(class: css_classes(:required_fields_notice, :container)) do
|
|
# TODO: Localize
|
|
p(class: css_classes(:required_fields_notice, :text)) { "* Indica campo requerido" }
|
|
end
|
|
end
|
|
|
|
def view_template
|
|
form_wrapper_class = css_classes(:form, :wrapper)
|
|
div(class: "#{form_wrapper_class} questions-#{form_wrapper_class}") do
|
|
div do
|
|
render_errors if errors.any? && render_top_error_bar
|
|
end
|
|
div do
|
|
form
|
|
end
|
|
end
|
|
|
|
div(class: css_classes(:form, :back_link_wrapper)) do
|
|
a(href: @questions_object.class.back_link, class: css_classes(:back_link)) { "Back" }
|
|
end
|
|
end
|
|
|
|
# TODO: FOR NOW we disable turbo for the submitting of this form, how we'll implement this will be a matter of a next iteration
|
|
def form
|
|
form_with(model: @questions_object, url: @form_url, method: @form_method, class: css_classes(:form, :main), data: { turbo: false }, html: { readonly: read_only }) do |form|
|
|
required_fields_message_notice
|
|
|
|
div(class: css_classes(:form, :fields_wrapper)) do
|
|
render_fields(form: form)
|
|
end
|
|
|
|
unless read_only
|
|
div(class: css_classes(:form, :submit_wrapper)) do
|
|
form.submit("Enviar", class: css_classes(:submit_button))
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def render_fields(form:)
|
|
@questions_object.questions.each do |question|
|
|
render_question(question: question, form: form)
|
|
end
|
|
end
|
|
|
|
def render_question(form:, question:)
|
|
div(class: css_classes(:question, :wrapper)) do
|
|
label(for: "sign_up_questions_#{question[:name]}", class: css_classes(:question, :label)) do
|
|
plain question[:title]
|
|
if question[:required]
|
|
# TODO: allow the required class
|
|
span(class: css_classes(:question, :required_notice)) { " *" }
|
|
end
|
|
end
|
|
|
|
case question[:type]
|
|
when :string
|
|
render_string(form: form, question: question)
|
|
when :text
|
|
render_text(form: form, question: question)
|
|
when :radio
|
|
render_radio(form: form, question: question)
|
|
when :boolean
|
|
render_boolean(form: form, question: question)
|
|
when :integer
|
|
render_integer(form: form, question: question)
|
|
when :select
|
|
render_select(form: form, question: question)
|
|
when :checkbox_group
|
|
render_checkbox_group(form: form, question: question)
|
|
else
|
|
# Fallback for any other type
|
|
render_string(form: form, question: question)
|
|
end
|
|
|
|
if question[:description].present?
|
|
div(class: css_classes(:question, :description)) { question[:description] }
|
|
end
|
|
|
|
if errors.include?(question[:name])
|
|
# TODO: Are we only showing one error here?
|
|
div(class: css_classes(:question, :error)) { errors[question[:name]].first }
|
|
end
|
|
end
|
|
end
|
|
|
|
def render_string(form:, question:)
|
|
input(
|
|
type: "text",
|
|
name: "sign_up_questions[#{question[:name]}]",
|
|
id: "sign_up_questions_#{question[:name]}",
|
|
class: css_classes(:input_fields, :text_input),
|
|
placeholder: "Enter your #{question[:title].downcase}",
|
|
value: question_value(question: question),
|
|
readonly: read_only,
|
|
disabled: read_only
|
|
)
|
|
end
|
|
|
|
def render_text(form:, question:)
|
|
textarea(
|
|
name: "sign_up_questions[#{question[:name]}]",
|
|
id: "sign_up_questions_#{question[:name]}",
|
|
class: css_classes(:input_fields, :textarea),
|
|
placeholder: "Enter your #{question[:title].downcase}",
|
|
rows: 3,
|
|
readonly: read_only,
|
|
disabled: read_only
|
|
) do
|
|
question_value(question: question)
|
|
end
|
|
end
|
|
|
|
# TODO: Make this a select with options or make a different type :radio_options or :select_options
|
|
def render_radio(form:, question:)
|
|
div(class: css_classes(:radio_group, :container)) do
|
|
question[:options].each do |value, label|
|
|
wrapper_class = question[:options].size <= 3 ? css_classes(:radio_group, :item_wrapper_inline) : css_classes(:radio_group, :item_wrapper)
|
|
div(class: wrapper_class) do
|
|
input(
|
|
type: "radio",
|
|
name: "sign_up_questions[#{question[:name]}]",
|
|
id: "sign_up_questions_#{question[:name]}_#{value}",
|
|
value: value,
|
|
class: css_classes(:radio_group, :input),
|
|
checked: question_value(question: question) == value,
|
|
disabled: read_only
|
|
)
|
|
label(
|
|
for: "sign_up_questions_#{question[:name]}_#{value}",
|
|
class: css_classes(:radio_group, :label)
|
|
) { label }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def render_boolean(form:, question:)
|
|
div(class: css_classes(:checkbox, :wrapper)) do
|
|
input(
|
|
type: "checkbox",
|
|
name: "sign_up_questions[#{question[:name]}]",
|
|
id: "sign_up_questions_#{question[:name]}",
|
|
value: "1",
|
|
class: css_classes(:checkbox, :input),
|
|
checked: question_value(question: question).present?,
|
|
disabled: read_only
|
|
)
|
|
label(
|
|
for: "sign_up_questions_#{question[:name]}",
|
|
class: css_classes(:checkbox, :label)
|
|
) { question[:description] || "Yes" }
|
|
end
|
|
end
|
|
|
|
def render_integer(form:, question:)
|
|
input(
|
|
type: "number",
|
|
name: "sign_up_questions[#{question[:name]}]",
|
|
id: "sign_up_questions_#{question[:name]}",
|
|
class: css_classes(:input_fields, :number_input),
|
|
value: question_value(question: question),
|
|
readonly: read_only,
|
|
disabled: read_only
|
|
)
|
|
end
|
|
|
|
def render_select(form:, question:)
|
|
select(
|
|
name: "sign_up_questions[#{question[:name]}]",
|
|
id: "sign_up_questions_#{question[:name]}",
|
|
class: css_classes(:input_fields, :select),
|
|
disabled: read_only
|
|
) do
|
|
option(value: "", disabled: true, selected: question_value(question: question).blank?) { "Seleccione una opci\u00f3n" }
|
|
question[:options].each do |value, label|
|
|
option(value: value, selected: question_value(question: question) == value) { label }
|
|
end
|
|
end
|
|
end
|
|
|
|
def render_checkbox_group(form:, question:)
|
|
div(class: css_classes(:checkbox_group, :container)) do
|
|
question[:options].each do |value, label|
|
|
div(class: css_classes(:checkbox_group, :item_wrapper)) do
|
|
input(
|
|
type: "checkbox",
|
|
name: "sign_up_questions[#{question[:name]}][]",
|
|
id: "sign_up_questions_#{question[:name]}_#{value}",
|
|
value: value,
|
|
class: css_classes(:checkbox_group, :input),
|
|
checked: Array(question_value(question: question)).include?(value),
|
|
disabled: read_only
|
|
)
|
|
label(
|
|
for: "sign_up_questions_#{question[:name]}_#{value}",
|
|
class: css_classes(:checkbox_group, :label)
|
|
) { label }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|
|
def question_value(question:)
|
|
value = questions_object.send(question[:name])
|
|
return value unless question[:type] == :checkbox_group
|
|
normalize_checkbox_group_value(value)
|
|
end
|
|
|
|
def normalize_checkbox_group_value(value)
|
|
case value
|
|
when Array
|
|
value
|
|
when String
|
|
if value.start_with?("[") && value.end_with?("]")
|
|
JSON.parse(value) rescue []
|
|
else
|
|
value.present? ? [ value ] : []
|
|
end
|
|
when NilClass, FalseClass
|
|
[]
|
|
else
|
|
Array(value.presence)
|
|
end
|
|
end
|
|
|
|
def render_errors
|
|
div(class: css_classes(:error_display, :container)) do
|
|
h4(class: css_classes(:error_display, :title)) { "#{pluralize(@questions_object.errors.count, 'error')} prohibited this form from being saved:" }
|
|
ul(class: css_classes(:error_display, :list)) do
|
|
@questions_object.errors.full_messages.each do |message|
|
|
li { message }
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
end
|
|
|