Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion app/controllers/sectors_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ def set_sector
# Strong parameters
def sector_params
params.require(:sector).permit(
:name, :published
:name, :published, :description
)
end
end
2 changes: 1 addition & 1 deletion app/helpers/application_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,7 @@ def form_section_bar_class
def dynamic_form_field_options(field)
case field.field_identifier
when *FormField::SERVICE_AREA_FIELD_IDENTIFIERS
field.service_area_sectors.map { |sector| [ sector.name, sector.id.to_s ] }
field.service_area_sectors.map { |sector| [ sector.name, sector.id.to_s, sector.description ] }
when *FormField::DYNAMIC_FIELD_CATEGORY_TYPES.keys
field.dynamic_categories.map { |category| [ category.name, category.id.to_s, category.description ] }
end
Expand Down
34 changes: 24 additions & 10 deletions app/models/sector.rb
Original file line number Diff line number Diff line change
@@ -1,26 +1,40 @@
class Sector < ApplicationRecord
include NameFilterable, Publishable
# Canonical service-area sector tags, in display order. "Other" is kept at the end
# as the catch-all free-text fallback for additional service areas (see
# OTHER_SECTOR_NAME below) — it isn't a selectable tag itself. Descriptions for
# these (the parenthetical clarifications) live in db/seeds.rb.
SECTOR_TYPES = [
"Child Abuse",
"Community Oppression/Violence",
"Criminal/Legal",
"Disability",
"Batterers Intervention",
"Child Abuse/Neglect",
"Climate/Environmental",
"Community Violence",
"Court/Legal System",
"Disability Services",
"Domestic Violence",
"Education/Schools",
"Education",
"Foster Care/Adoption",
"Homeless",
"Fundraising/Donor Engagement",
"Grief/Loss",
"Health/Medical",
"Homelessness",
"Human Trafficking",
"Immigration",
"Incarceration",
"Indigenous/Tribal Nation",
"LGBTQIA+",
"Mental Health",
"Reproductive",
"Military/Veterans",
"Private Practice/Sole Proprietor",
"Racial/Social Justice",
"Religious/Faith Based",
"Reproductive Services",
"Restorative/Transformative Justice",
"Self-Care/Personal Growth",
"Sexual Assault",
"Student",
"Substance Use",
"Veterans & Military",
"Staff/Organizational Development",
"Substance Use/Recovery",
"Systems/Policy Change",
"Other"
]

Expand Down
11 changes: 7 additions & 4 deletions app/views/events/public_registrations/_form_field.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -116,17 +116,20 @@
class="<%= input_classes %>"
<%= "required" if field.required %>>
<option value="">Select one</option>
<% dropdown_options.each do |option_label, option_value| %>
<option value="<%= option_value %>" <%= "selected" if value == option_value %>><%= option_label %></option>
<%# A dropdown can't show subtext, so an option's description is folded into
the label as "Name (description)". %>

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: A dropdown can't show subtext, so a sector/category description is folded into the label as "Name (description)" here; the checkbox/radio renderers instead show it as gray subtext under the option.

<% dropdown_options.each do |option_label, option_value, option_description| %>
<% display_label = option_description.present? ? "#{option_label} (#{option_description})" : option_label %>
<option value="<%= option_value %>" <%= "selected" if value == option_value %>><%= display_label %></option>
<% end %>
</select>

<% when "multi_select_checkbox" %>
<% checkbox_name = "public_registration[form_fields][#{field.id}][]" %>
<% selected_values = Array(value) %>
<%# Dynamic fields (e.g. primary_service_area) source options from Sector/Category
data; everything else uses the field's own stored answer options. Category
options carry an optional description, shown as subtext under the label. %>
data; everything else uses the field's own stored answer options. Sector and
Category options carry an optional description, shown as subtext under the label. %>
<% checkbox_options = dynamic_form_field_options(field) ||
field.form_field_answer_options.includes(:answer_option).map { |ffao| [ ffao.answer_option.name, ffao.answer_option.name ] } %>
<% has_specify = checkbox_options.any? { |option_label, _option_value| specify_placeholder(option_label).present? } %>
Expand Down
9 changes: 9 additions & 0 deletions app/views/sectors/_form.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,15 @@

</div>

<!-- Description -->
<div>
<%= f.input :description,
as: :text,
label: "Description",
hint: "Optional. Shown under this sector on the public registration form to clarify its meaning when the name alone isn't enough.",
input_html: { class: "form-control", rows: 2 } %>
</div>

<div class="action-buttons mt-8 flex justify-center gap-3">
<% if allowed_to?(:destroy?, f.object) %>
<%= link_to "Delete", @sector, class: "btn btn-danger-outline",
Expand Down
4 changes: 4 additions & 0 deletions app/views/sectors/index.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,10 @@

<td class="px-4 py-2 text-md text-gray-800 truncate font-bold">
<%= sector.name %>
<% if sector.description.present? %>
<i class="fa-solid fa-circle-info ml-1 text-sm font-normal text-gray-400 hover:text-blue-600 cursor-help"
title="<%= sector.description %>"></i>
<% end %>
</td>

<td class="px-4 py-2 text-center">
Expand Down
7 changes: 7 additions & 0 deletions app/views/sectors/show.html.erb
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,13 @@
</div>

</div>

<% if @sector.description.present? %>
<div class="mb-3">
<p class="font-bold text-gray-700">Description:</p>
<p class="text-gray-900"><%= @sector.description %></p>
</div>
<% end %>
</div>
</div>
</div>
9 changes: 9 additions & 0 deletions db/migrate/20260616130000_add_description_to_sectors.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
class AddDescriptionToSectors < ActiveRecord::Migration[8.1]
def up
add_column :sectors, :description, :text unless column_exists?(:sectors, :description)
end

def down
remove_column :sectors, :description, :text, if_exists: true
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema[8.1].define(version: 2026_06_16_120000) do
ActiveRecord::Schema[8.1].define(version: 2026_06_16_130000) do
create_table "action_text_mentions", charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.bigint "action_text_rich_text_id", null: false
t.datetime "created_at", null: false
Expand Down Expand Up @@ -1099,6 +1099,7 @@

create_table "sectors", id: :integer, charset: "utf8mb4", collation: "utf8mb4_unicode_ci", force: :cascade do |t|
t.datetime "created_at", precision: nil, null: false
t.text "description"
t.string "name"
t.boolean "published", default: false
t.datetime "updated_at", precision: nil, null: false
Expand Down
26 changes: 25 additions & 1 deletion db/seeds.rb
Original file line number Diff line number Diff line change
Expand Up @@ -149,11 +149,35 @@ def find_or_create_by_name!(klass, name, **attrs, &block)
end

puts "Creating Sectors…"
# Optional descriptions clarify a sector on the public registration form: shown as
# subtext under the checkbox in the additional service-areas list, and folded into
# the "Name (description)" label in the single primary service-area dropdown (which
# can't show subtext). They come from the parenthetical clarifications on the
# canonical sector list. Names without an entry below have no description. Admins
# can edit each from the Sectors admin once seeded.
sector_descriptions = {
"Climate/Environmental" => "fire recovery, disaster response, environmental trauma",
"Community Violence" => "gang violence, police violence, mass shootings, etc.",
"Health/Medical" => "hospitals, first responders, illness and chronic disease",
"Immigration" => "family separation, deportation, refugees/asylees, etc.",
"Incarceration" => "including re-entry services",
"Reproductive Services" => "birth trauma, perinatal care, challenges conceiving, etc.",
"Restorative/Transformative Justice" => "individual and community reconciliation",
"Staff/Organizational Development" => "Secondary/Vicarious Trauma",
"Systems/Policy Change" => "Advocating at state/government levels for policy change"
}
Sector::SECTOR_TYPES.each do |sector_type|
sector = find_or_create_by_name!(Sector, sector_type)
sector.update!(published: true) unless sector.published?
sector.update!(published: true, description: sector_descriptions[sector_type])
end

# Unpublish any sector no longer on the canonical list, preserving its historical
# taggings rather than destroying them. SECTOR_TYPES already includes the "Other"
# catch-all, so it stays published.
canonical_names = Sector::SECTOR_TYPES.map(&:downcase)
Sector.reject { |sector| canonical_names.include?(sector.name.downcase) }

Copy link
Copy Markdown
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤖 From Claude: Sectors dropped from the canonical list are unpublished here, not destroyed, so historical taggings survive. Other stays published because it's the free-text catch-all for additional service areas, not a selectable tag.

.each { |sector| sector.update!(published: false) }

puts "Creating CategoryTypes/Categories…"
category_type_categories = [
[ "AgeRange", "3-5" ],
Expand Down
10 changes: 10 additions & 0 deletions spec/helpers/application_helper_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -423,6 +423,16 @@
expect(labels).to include("Other")
end

it "carries each sector's description as the third tuple element" do
create(:sector, :published, name: "Domestic Violence", description: "DV services")
create(:sector, :published, name: "Mental Health", description: nil)
field = create(:form_field, form: form, answer_type: :multi_select_checkbox, field_identifier: "primary_service_area")

descriptions = helper.dynamic_form_field_options(field).to_h { |name, _id, desc| [ name, desc ] }
expect(descriptions["Domestic Violence"]).to eq("DV services")
expect(descriptions["Mental Health"]).to be_nil
end

it "omits the Mixed-age groups category for the primary age group field" do
type = create(:category_type, name: "AgeRange")
create(:category, :published, category_type: type, name: "3-5")
Expand Down
6 changes: 4 additions & 2 deletions spec/requests/sectors_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -122,15 +122,17 @@
context "with valid parameters" do
let(:new_attributes) do
valid_attributes.merge(
name: "Updated Sector Name"
name: "Updated Sector Name",
description: "Clarifying subtext for this sector"
)
end

it "updates the requested sector" do
sector = Sector.create! valid_attributes
patch sector_url(sector), params: { sector: new_attributes }
sector.reload
skip("Add assertions for updated state")
expect(sector.name).to eq("Updated Sector Name")
expect(sector.description).to eq("Clarifying subtext for this sector")
end

it "redirects to the sector" do
Expand Down