diff --git a/AGENTS.md b/AGENTS.md index 4965990ed..4599b4c3b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -77,6 +77,10 @@ AWBW Portal (Rails 8.1) | `Person` | Organization affiliates with contacts, addresses, sectors | | `Organization` | Groups with affiliations, addresses, logos via ActiveStorage | | `Report` | STI base class for MonthlyReport and WorkshopLog | +| `Form` | Configurable forms with sections, conditional visibility, nested form fields | +| `FormField` | Individual fields on a form with visibility, input type, and ordering | +| `FormSubmission` | A person's submission of a form (replaces PersonForm) | +| `FormAnswer` | A single answer within a form submission (replaces PersonFormFormField) | ### STI Models @@ -152,6 +156,7 @@ end - `TaggingSearchService` — Search and filter tagging data - `PersonFromUserService` — Create Person from User account - `BulkInviteService` — Bulk send welcome instructions and reset created_at for users +- `FormBuilderService` — Builds configurable forms from composable sections with per-field visibility - `ModelDeduper` — Deduplication logic - `NotificationServices::CreateNotification` — Notification creation - `NotificationServices::PersistDeliveredEmail` — Email delivery tracking @@ -212,7 +217,7 @@ end - **Always use Tailwind CSS** utility classes for styling — do not write custom CSS unless absolutely necessary - Prefer Turbo for navigation and form submissions before reaching for Stimulus -### Stimulus Controllers (32) +### Stimulus Controllers (34) Key controllers: - `asset_picker` — Asset selection UI @@ -225,6 +230,8 @@ Key controllers: - `optimistic_bookmark` — Instant bookmark UI feedback - `remote_select` — AJAX-powered select dropdown - `searchable_select` — Tom Select autocomplete +- `chip_select` — Dynamically styles ` as a colored chip based on its current value. + * + * Usage: + * <% @registration_forms.each do |reg_form| %> <% label = case reg_form.name - when ExtendedEventRegistrationFormBuilder::FORM_NAME, "Public Registration" then "Extended registration form" - when ShortEventRegistrationFormBuilder::FORM_NAME, "Short Registration" then "Short registration form" + when "Extended Event Registration", "Public Registration" then "Extended registration form" + when "Short Event Registration", "Short Registration" then "Short registration form" else reg_form.name end %> <% end %> - <% if @event.cost_cents.to_i > 0 %> -

Scholarship application form will be linked automatically for paid events.

- <% end %> +

+ <%= link_to "Create new form", new_form_path, class: "text-blue-600 hover:text-blue-800 underline", target: "_blank" %> + <% if @event.cost_cents.to_i > 0 %> + · Scholarship application form will be linked automatically for paid events. + <% end %> +

diff --git a/app/views/events/_manage_results.html.erb b/app/views/events/_manage_results.html.erb index 3b4c0707f..5d4c42e88 100644 --- a/app/views/events/_manage_results.html.erb +++ b/app/views/events/_manage_results.html.erb @@ -14,7 +14,7 @@ <% event_form_ids = @event.form_ids %> - <% form_submissions = event_form_ids.any? ? PersonForm.joins(:form).where(form_id: event_form_ids, person_id: @event_registrations.map(&:registrant_id)).pluck(:person_id, :created_at, Arel.sql("forms.name")).group_by(&:first).transform_values { |rows| rows.map { |_, ts, name| [ name, ts ] } } : {} %> + <% form_submissions = event_form_ids.any? ? FormSubmission.joins(:form).where(form_id: event_form_ids, person_id: @event_registrations.map(&:registrant_id)).pluck(:person_id, :created_at, Arel.sql("forms.name")).group_by(&:first).transform_values { |rows| rows.map { |_, ts, name| [ name, ts ] } } : {} %> <% if @event_registrations.any? %>
diff --git a/app/views/events/public_registrations/_form_field.html.erb b/app/views/events/public_registrations/_form_field.html.erb index d5fe9730f..50c912184 100644 --- a/app/views/events/public_registrations/_form_field.html.erb +++ b/app/views/events/public_registrations/_form_field.html.erb @@ -1,4 +1,4 @@ -<%# locals: (field:, value: nil) %> +<%# locals: (field:, value: nil, label: nil) %> <% return if field.group_header? %> <% error = @field_errors&.dig(field.id) %> @@ -6,14 +6,14 @@
- <% if field.instructional_hint.present? %> -

<%= field.instructional_hint %>

+ <% if field.hint_text.present? %> +

<%= field.hint_text %>

<% end %> <% field_name = "public_registration[form_fields][#{field.id}]" %> @@ -21,11 +21,11 @@ <% case field.answer_type %> <% when "free_form_input_one_line" %> - <% if field.field_key&.end_with?("_state") %> + <% if field.field_identifier&.end_with?("_state") %> <% else %> <% - input_type = case field.field_key + html_type = case field.field_identifier when /email(?!_type)/ then "email" when "phone" then "tel" when "agency_website" then "url" @@ -43,12 +43,12 @@ extra_attrs = [] extra_attrs << 'min="1" step="1" pattern="[0-9]*" inputmode="numeric"' if field.number_integer? %> - + <%= "required" if field.required %> <%= raw(extra_attrs.join(" ")) %>> <% end %> @@ -57,7 +57,7 @@ id="<%= field_id %>" rows="4" class="w-full rounded-md border <%= error_border %> px-3 py-2 text-sm focus:border-blue-500 focus:ring-1 focus:ring-blue-500" - <%= "required" if field.is_required %>><%= value %> + <%= "required" if field.required %>><%= value %> <% when "multiple_choice_radio" %>
@@ -67,8 +67,8 @@ name="<%= field_name %>" value="<%= ffao.answer_option.name %>" class="text-blue-600 focus:ring-blue-500" - <%= "checked" if value == ffao.answer_option.name || (value.blank? && field.field_key == "interested_in_more" && ffao.answer_option.name == "Yes") %> - <%= "required" if field.is_required %>> + <%= "checked" if value == ffao.answer_option.name || (value.blank? && field.field_identifier == "interested_in_more" && ffao.answer_option.name == "Yes") %> + <%= "required" if field.required %>> <%= ffao.answer_option.name %> <% end %> @@ -78,9 +78,9 @@ <% checkbox_name = "public_registration[form_fields][#{field.id}][]" %> <% selected_values = Array(value) %> - <% if field.field_key.in?(%w[primary_service_area workshop_environments client_life_experiences primary_age_group]) %> + <% if field.field_identifier.in?(%w[primary_service_area workshop_environments client_life_experiences primary_age_group]) %> <%# Professional fields: render dynamic sector/category options %> - <% if field.field_key == "primary_service_area" %> + <% if field.field_identifier == "primary_service_area" %> <% options = Sector.published.order(:name) %>
<% options.each do |sector| %> @@ -100,7 +100,7 @@ "workshop_environments" => "WorkshopEnvironment", "client_life_experiences" => "StoryPopulation", "primary_age_group" => "AgeRange" - }[field.field_key] %> + }[field.field_identifier] %> <% ct = CategoryType.find_by(name: category_type_name) %> <% options = ct&.categories&.published&.order(:position, :name) || [] %>
@@ -135,6 +135,6 @@ <% end %> <% if error %> -

<%= field.question %> <%= error %>

+

<%= field.name %> <%= error %>

<% end %>
diff --git a/app/views/events/public_registrations/new.html.erb b/app/views/events/public_registrations/new.html.erb index bd5974a33..244a90d50 100644 --- a/app/views/events/public_registrations/new.html.erb +++ b/app/views/events/public_registrations/new.html.erb @@ -62,8 +62,10 @@ <%# Row groupings: related fields share a grid row on md+ screens %> <% - fields_by_key_check = @form_fields.select(&:field_key).index_by(&:field_key) - email_row = if fields_by_key_check["confirm_email"] + fields_by_id_check = @form_fields.select(&:field_identifier).index_by(&:field_identifier) + email_row = if fields_by_id_check["confirm_email"] && fields_by_id_check["primary_email_type"] + { keys: %w[primary_email confirm_email primary_email_type], grid: "grid grid-cols-1 md:grid-cols-3 gap-4" } + elsif fields_by_id_check["confirm_email"] { keys: %w[primary_email confirm_email], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" } else { keys: %w[primary_email primary_email_type], grid: "grid grid-cols-1 md:grid-cols-3 gap-4", spans: { "primary_email" => "md:col-span-2" } } @@ -74,6 +76,7 @@ { keys: %w[nickname pronouns], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, email_row, { keys: %w[secondary_email secondary_email_type], grid: "grid grid-cols-1 md:grid-cols-3 gap-4", spans: { "secondary_email" => "md:col-span-2" } }, + { keys: %w[mailing_street mailing_address_type], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, { keys: %w[mailing_city mailing_state mailing_zip], grid: "grid grid-cols-1 md:grid-cols-3 gap-4" }, { keys: %w[phone phone_type], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, { keys: %w[agency_name agency_position], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, @@ -81,34 +84,42 @@ { keys: %w[number_of_attendees payment_method], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, ] - field_key_to_group = {} - row_groups.each { |g| g[:keys].each { |k| field_key_to_group[k] = g } } + identifier_to_group = {} + row_groups.each { |g| g[:keys].each { |k| identifier_to_group[k] = g } } - fields_by_key = @form_fields.select(&:field_key).index_by(&:field_key) - rendered_keys = {} + fields_by_identifier = @form_fields.select(&:field_identifier).index_by(&:field_identifier) + rendered_ids = {} + + has_secondary_email = fields_by_identifier["secondary_email"].present? + email_label_overrides = if has_secondary_email + { "primary_email" => "Primary Email", "confirm_email" => "Confirm Primary Email", "primary_email_type" => "Primary Email Type" } + else + {} + end %> <% @form_fields.each do |field| %> - <% next if field.field_key.present? && rendered_keys[field.field_key] %> + <% next if field.field_identifier.present? && rendered_ids[field.field_identifier] %> <% if field.group_header? %>
-

<%= field.question %>

+

<%= field.name %>

- <% elsif (group = field.field_key.present? && field_key_to_group[field.field_key]) %> + <% elsif (group = field.field_identifier.present? && identifier_to_group[field.field_identifier]) %>
<% group[:keys].each do |key| %> - <% row_field = fields_by_key[key] %> + <% row_field = fields_by_identifier[key] %> <% next unless row_field %> - <% rendered_keys[key] = true %> + <% rendered_ids[key] = true %> <% span = group.dig(:spans, key) %> <% submitted_value = params.dig(:public_registration, :form_fields, row_field.id.to_s) %> + <% label_override = email_label_overrides[key] %> <% if span %>
- <%= render "events/public_registrations/form_field", field: row_field, value: submitted_value %> + <%= render "events/public_registrations/form_field", field: row_field, value: submitted_value, label: label_override %>
<% else %> - <%= render "events/public_registrations/form_field", field: row_field, value: submitted_value %> + <%= render "events/public_registrations/form_field", field: row_field, value: submitted_value, label: label_override %> <% end %> <% end %>
diff --git a/app/views/events/public_registrations/show.html.erb b/app/views/events/public_registrations/show.html.erb index 94373ae41..ad2c03d50 100644 --- a/app/views/events/public_registrations/show.html.erb +++ b/app/views/events/public_registrations/show.html.erb @@ -1,6 +1,6 @@ <% content_for(:page_bg_class, "public") %> -<% reg = @person_form.person.event_registrations.find_by(event: @event) %> +<% reg = @form_submission.person.event_registrations.find_by(event: @event) %> <% back_path = reg&.slug.present? ? registration_ticket_path(reg.slug) : event_path(@event) %>
<%= link_to "← Back to Registration", back_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> @@ -32,6 +32,7 @@ { keys: %w[nickname pronouns], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, { keys: %w[primary_email primary_email_type], grid: "grid grid-cols-1 md:grid-cols-3 gap-4", spans: { "primary_email" => "md:col-span-2" } }, { keys: %w[secondary_email secondary_email_type], grid: "grid grid-cols-1 md:grid-cols-3 gap-4", spans: { "secondary_email" => "md:col-span-2" } }, + { keys: %w[mailing_street mailing_address_type], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, { keys: %w[mailing_city mailing_state mailing_zip], grid: "grid grid-cols-1 md:grid-cols-3 gap-4" }, { keys: %w[phone phone_type], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, { keys: %w[agency_name agency_position], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, @@ -39,30 +40,31 @@ { keys: %w[number_of_attendees payment_method], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, ] - field_key_to_group = {} - row_groups.each { |g| g[:keys].each { |k| field_key_to_group[k] = g } } + identifier_to_group = {} + row_groups.each { |g| g[:keys].each { |k| identifier_to_group[k] = g } } - fields_by_key = @form_fields.select(&:field_key).index_by(&:field_key) - rendered_keys = {} + fields_by_identifier = @form_fields.select(&:field_identifier).index_by(&:field_identifier) + rendered_ids = {} %> <% @form_fields.each do |field| %> - <% next if field.field_key.present? && rendered_keys[field.field_key] %> + <% next if field.field_identifier == "confirm_email" %> + <% next if field.field_identifier.present? && rendered_ids[field.field_identifier] %> <% if field.group_header? %>
-

<%= field.question %>

+

<%= field.name %>

- <% elsif (group = field.field_key.present? && field_key_to_group[field.field_key]) %> + <% elsif (group = field.field_identifier.present? && identifier_to_group[field.field_identifier]) %>
<% group[:keys].each do |key| %> - <% row_field = fields_by_key[key] %> + <% row_field = fields_by_identifier[key] %> <% next unless row_field %> - <% rendered_keys[key] = true %> + <% rendered_ids[key] = true %> <% response = @responses[row_field.id] %> <% span = group.dig(:spans, key) %>
-
<%= row_field.question %>
+
<%= row_field.name %>
<%= display_response_text(row_field, response) %>
@@ -72,7 +74,7 @@ <% else %> <% response = @responses[field.id] %>
-
<%= field.question %>
+
<%= field.name %>
<%= display_response_text(field, response) %>
@@ -82,7 +84,7 @@

- Submitted on <%= @person_form.created_at.strftime("%B %d, %Y at %l:%M %P") %> + Submitted on <%= @form_submission.created_at.strftime("%B %d, %Y at %l:%M %P") %>

diff --git a/app/views/forms/_form_field_fields.html.erb b/app/views/forms/_form_field_fields.html.erb new file mode 100644 index 000000000..50dfd5e44 --- /dev/null +++ b/app/views/forms/_form_field_fields.html.erb @@ -0,0 +1,75 @@ +<% + field = f.object + + visibility_options = [ + [ "Always ask", "always_ask" ], + [ "Scholarship-only", "scholarship_only" ], + [ "Logged out only", "logged_out_only" ], + [ "Answers on file", "answers_on_file" ] + ] + + visibility_styles = { + always_ask: "bg-white text-gray-600 border-gray-300", + scholarship_only: "bg-purple-100 text-purple-700 border-purple-200", + logged_out_only: "bg-emerald-100 text-emerald-700 border-emerald-200", + answers_on_file: "bg-amber-100 text-amber-700 border-amber-200" + } +%> +
data-section="<%= field.section %>"<% end %> + <% if field.group_header? %>data-group-header<% end %>> +
+ +
+ + <%= f.hidden_field :id if field.persisted? %> + <%= f.hidden_field :position, value: field.position %> + + <% if field.group_header? %> +
+ <%= f.select :visibility, visibility_options, + {}, + class: "text-xs rounded-full border px-2 py-0.5 cursor-pointer", + data: { controller: "chip-select", chip_select_styles_value: visibility_styles, action: "chip-select#update" } %> + <%= f.text_field :name, required: true, class: "flex-1 text-lg font-semibold text-gray-800 rounded border-gray-300 shadow-sm px-2 py-1" %> +
+ <%= link_to_remove_association "Remove", f, + class: "text-sm text-gray-400 hover:text-red-600 underline" %> +
+
+ <% else %> +
+ <%= f.select :visibility, visibility_options, + {}, + class: "text-xs rounded-full border px-2 py-0.5 shrink-0 cursor-pointer", + data: { controller: "chip-select", chip_select_styles_value: visibility_styles, action: "chip-select#update" } %> + <%= f.text_field :name, required: true, class: "min-w-0 flex-[3_1_10rem] rounded border-gray-300 shadow-sm px-2 py-1 text-sm" %> + <% + type_order = %w[free_form_input_one_line free_form_input_paragraph multiple_choice_radio multiple_choice_checkbox no_user_input group_header] + type_labels = { + "group_header" => "Section header", + "free_form_input_one_line" => "One line", + "free_form_input_paragraph" => "Paragraph", + "multiple_choice_radio" => "Multiple choice radio", + "multiple_choice_checkbox" => "Multiple choice checkbox", + "no_user_input" => "Informational-only" + } + type_options = type_order.map { |t| [ type_labels[t] || t.titleize.gsub("_", " "), t ] } + %> + <%= f.select :answer_type, type_options, + {}, + class: "flex-[2_1_11rem] rounded border-gray-300 shadow-sm px-2 py-1 text-sm" %> + + + <%= link_to_remove_association "Remove", f, + class: "text-sm text-gray-400 hover:text-red-600 underline shrink-0 ml-auto" %> +
+ <% end %> +
diff --git a/app/views/forms/edit.html.erb b/app/views/forms/edit.html.erb new file mode 100644 index 000000000..1c4aa6951 --- /dev/null +++ b/app/views/forms/edit.html.erb @@ -0,0 +1,88 @@ +
+
+ <%= link_to "Forms", forms_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> + <%= link_to "View form", form_path(@form), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> + <%= link_to "Edit sections", edit_sections_form_path(@form), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> +
+

Edit: <%= @form.display_name %>

+ + <%= form_with model: @form, class: "space-y-6" do |f| %> + <% if @form.errors.any? %> +
+

+ <%= pluralize(@form.errors.count, "error") %> prevented this form from being saved: +

+
    + <% @form.errors.full_messages.each do |message| %> +
  • <%= message %>
  • + <% end %> +
+
+ <% end %> + +
+
+ + <%= f.text_field :name, class: "w-full rounded border-gray-300 shadow-sm px-3 py-2 text-sm" %> +
+ +
+ + +
+
+ +
+
+

Fields

+ <%= @form_fields.size %> fields +
+ +
+

Conditional visibility

+
+
+ Always ask + Always shown to all users +
+
+ Scholarship-only + Shown only when registering for scholarship +
+
+ Logged out only + Hidden for logged-in users +
+
+ Answers on file + Hidden when answered on this event's forms; if "One-time", hidden when answered on any form +
+
+
+ +
+ <%= f.fields_for :form_fields, @form_fields do |ff| %> + <%= render "form_field_fields", f: ff %> + <% end %> +
+ +
+ <%= link_to_add_association "+ Add field", f, :form_fields, + data: { association_insertion_node: "[data-controller='form-fields-sortable']", + association_insertion_method: "append" }, + class: "text-sm text-blue-600 hover:text-blue-800 font-medium" %> +
+
+ +
+ <%= f.submit "Save Changes", class: "btn btn-primary cursor-pointer" %> + <%= link_to "Cancel", forms_path, class: "btn btn-secondary-outline" %> +
+ <% end %> +
diff --git a/app/views/forms/edit_sections.html.erb b/app/views/forms/edit_sections.html.erb new file mode 100644 index 000000000..62c976c4b --- /dev/null +++ b/app/views/forms/edit_sections.html.erb @@ -0,0 +1,36 @@ +
+
+ <%= link_to "Forms", forms_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> + <%= link_to "Edit fields", edit_form_path(@form), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> + <%= link_to "View form", form_path(@form), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> +
+

Edit Sections: <%= @form.display_name %>

+ + <% current_sections = (@form.sections || []).map(&:to_s) %> + + <%= form_with url: update_sections_form_path(@form), method: :patch, class: "space-y-6" do |f| %> +
+ Select sections to include: +
+ <% FormBuilderService::SECTIONS.each do |key, section| %> + + <% end %> +
+
+ +

+ Unchecking a section will remove its fields from the form. Checking a new section will add its default fields at the end. + Existing answers are preserved via question text snapshots. +

+ +
+ <%= f.submit "Update Sections", class: "btn btn-primary cursor-pointer" %> + <%= link_to "Cancel", edit_form_path(@form), class: "btn btn-secondary-outline" %> +
+ <% end %> +
diff --git a/app/views/forms/index.html.erb b/app/views/forms/index.html.erb new file mode 100644 index 000000000..303ba23a1 --- /dev/null +++ b/app/views/forms/index.html.erb @@ -0,0 +1,34 @@ +
+
+

Forms

+ <%= link_to "New Form", new_form_path, class: "btn btn-primary" %> +
+ +
+
+ + + + + + + + + + + <% @forms.each do |form| %> + + + + + + + + <% end %> + +
NameFieldsEventsSubmissions
<%= form.display_name %><%= form.form_fields.size %><%= form.events.size %><%= form.form_submissions.size %> + <%= link_to "View", form_path(form), class: "text-blue-600 hover:text-blue-800 underline" %> + <%= link_to "Edit", edit_form_path(form), class: "text-blue-600 hover:text-blue-800 underline" %> +
+
+ diff --git a/app/views/forms/new.html.erb b/app/views/forms/new.html.erb new file mode 100644 index 000000000..eb123abfe --- /dev/null +++ b/app/views/forms/new.html.erb @@ -0,0 +1,39 @@ +
+

New Form

+ + <%= form_with url: forms_path, method: :post, class: "space-y-6" do |f| %> +
+ + +
+ +
+ Select sections to include: +
+ <% FormBuilderService::SECTIONS.each do |key, section| %> + + <% end %> +
+
+ +
+ +
+ +
+ <%= f.submit "Create Form", class: "btn btn-primary cursor-pointer" %> + <%= link_to "Cancel", forms_path, class: "btn btn-secondary-outline" %> +
+ <% end %> +
diff --git a/app/views/forms/show.html.erb b/app/views/forms/show.html.erb new file mode 100644 index 000000000..f175d9756 --- /dev/null +++ b/app/views/forms/show.html.erb @@ -0,0 +1,158 @@ +
+
+ <%= link_to "Forms", forms_path, class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> + <%= link_to "Edit", edit_form_path(@form), class: "text-sm text-gray-500 hover:text-gray-700 px-2 py-1" %> +
+

<%= @form.display_name %>

+ +
+
+
+
+ <%= image_tag("logo.png", alt: "Organization logo", class: "h-8 w-auto") %> +
+

<%= @form.display_name %>

+
+
+ +
+ <% + has_scholarship = @form.form_fields.where(visibility: :scholarship_only).exists? + has_logged_out = @form.form_fields.where(visibility: :logged_out_only).exists? + has_answers_on_file = @form.form_fields.where(visibility: :answers_on_file).exists? + has_conditions = has_scholarship || (has_logged_out && @form.hide_answered_person_questions?) || (has_answers_on_file && @form.hide_answered_form_questions?) + + preview_logged_in = params[:preview_logged_in].present? + preview_scholarship = params[:preview_scholarship].present? + preview_answered = params[:preview_answered].present? + %> + +
+

Preview mode

+ <% if has_conditions %> +

Toggle to simulate how the form appears under different conditions:

+
+ <% if has_logged_out && @form.hide_answered_person_questions? %> + <% + logged_in_params = request.query_parameters.dup + if preview_logged_in + logged_in_params.delete("preview_logged_in") + else + logged_in_params["preview_logged_in"] = "1" + end + %> + <%= link_to form_path(@form, logged_in_params), class: "flex items-center gap-2 no-underline" do %> + User logged in + + + + <% end %> + <% end %> + <% if has_answers_on_file && @form.hide_answered_form_questions? %> + <% + answered_params = request.query_parameters.dup + if preview_answered + answered_params.delete("preview_answered") + else + answered_params["preview_answered"] = "1" + end + %> + <%= link_to form_path(@form, answered_params), class: "flex items-center gap-2 no-underline" do %> + Answers on file + + + + <% end %> + <% end %> + <% if has_scholarship %> + <% + scholarship_params = request.query_parameters.dup + if preview_scholarship + scholarship_params.delete("preview_scholarship") + else + scholarship_params["preview_scholarship"] = "1" + end + %> + <%= link_to form_path(@form, scholarship_params), class: "flex items-center gap-2 no-underline" do %> + Scholarship + + + + <% end %> + <% end %> +
+ <% else %> +

No conditional visibility configured. All fields always shown.

+ <% end %> +
+ + <% + fields_by_id_check = @form_fields.select(&:field_identifier).index_by(&:field_identifier) + email_row = if fields_by_id_check["confirm_email"] && fields_by_id_check["primary_email_type"] + { keys: %w[primary_email confirm_email primary_email_type], grid: "grid grid-cols-1 md:grid-cols-3 gap-4" } + elsif fields_by_id_check["confirm_email"] + { keys: %w[primary_email confirm_email], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" } + else + { keys: %w[primary_email primary_email_type], grid: "grid grid-cols-1 md:grid-cols-3 gap-4", spans: { "primary_email" => "md:col-span-2" } } + end + + row_groups = [ + { keys: %w[first_name last_name], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, + { keys: %w[nickname pronouns], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, + email_row, + { keys: %w[secondary_email secondary_email_type], grid: "grid grid-cols-1 md:grid-cols-3 gap-4", spans: { "secondary_email" => "md:col-span-2" } }, + { keys: %w[mailing_street mailing_address_type], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, + { keys: %w[mailing_city mailing_state mailing_zip], grid: "grid grid-cols-1 md:grid-cols-3 gap-4" }, + { keys: %w[phone phone_type], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, + { keys: %w[agency_name agency_position], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" }, + { keys: %w[agency_city agency_state agency_zip], grid: "grid grid-cols-1 md:grid-cols-3 gap-4" }, + { keys: %w[number_of_attendees payment_method], grid: "grid grid-cols-1 md:grid-cols-2 gap-4" } + ] + + identifier_to_group = {} + row_groups.each { |g| g[:keys].each { |k| identifier_to_group[k] = g } } + + fields_by_identifier = @form_fields.select(&:field_identifier).index_by(&:field_identifier) + rendered_ids = {} + + has_secondary_email = fields_by_identifier["secondary_email"].present? + email_label_overrides = if has_secondary_email + { "primary_email" => "Primary Email", "confirm_email" => "Confirm Primary Email", "primary_email_type" => "Primary Email Type" } + else + {} + end + %> + +
+ <% @form_fields.each do |field| %> + <% next if field.field_identifier.present? && rendered_ids[field.field_identifier] %> + + <% if field.group_header? %> +
+

<%= field.name %>

+
+ <% elsif (group = field.field_identifier.present? && identifier_to_group[field.field_identifier]) %> +
+ <% group[:keys].each do |key| %> + <% row_field = fields_by_identifier[key] %> + <% next unless row_field %> + <% rendered_ids[key] = true %> + <% span = group.dig(:spans, key) %> + <% label_override = email_label_overrides[key] %> + <% if span %> +
+ <%= render "events/public_registrations/form_field", field: row_field, value: nil, label: label_override %> +
+ <% else %> + <%= render "events/public_registrations/form_field", field: row_field, value: nil, label: label_override %> + <% end %> + <% end %> +
+ <% else %> + <%= render "events/public_registrations/form_field", field: field, value: nil %> + <% end %> + <% end %> +
+
+
+
diff --git a/app/views/monthly_reports/_form.html.erb b/app/views/monthly_reports/_form.html.erb index 097b2d934..085c787f5 100644 --- a/app/views/monthly_reports/_form.html.erb +++ b/app/views/monthly_reports/_form.html.erb @@ -122,8 +122,8 @@ <% @report.log_fields.each do |field| %> <%= render 'shared/form_field', :field => field, :report => @report %> - <% if field.question == 'Share challenges for this month' || - (field.question == 'Anything we can do to help you?' and params[:form_builder_id].to_i == 2) %> + <% if field.name == 'Share challenges for this month' || + (field.name == 'Anything we can do to help you?' and params[:form_builder_id].to_i == 2) %>
- <% if field.question == 'Share challenges for this month' || - (field.question == 'Anything we can do to help you?' and params[:form_builder_id].to_i == 2)%> + <% if field.name == 'Share challenges for this month' || + (field.name == 'Anything we can do to help you?' and params[:form_builder_id].to_i == 2)%> - <% if field.question == 'Share challenges for this month' || - (field.question == 'Anything we can do to help you?' and params[:form_builder_id].to_i == 2)%> + <% if field.name == 'Share challenges for this month' || + (field.name == 'Anything we can do to help you?' and params[:form_builder_id].to_i == 2)%>