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

# 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