diff --git a/Gemfile b/Gemfile index aeeb9e7df..749e89a19 100644 --- a/Gemfile +++ b/Gemfile @@ -53,6 +53,7 @@ gem 'premailer-rails' gem "bcrypt", '3.1.16' gem "json", ">= 2.6", "< 3" # or simply: gem "json", "~> 2.7" gem 'simple_form' +gem 'country_select' group :development, :test do gem 'better_errors' diff --git a/Gemfile.lock b/Gemfile.lock index c2e68a4d8..2487ea305 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -138,6 +138,10 @@ GEM execjs coffee-script-source (1.12.2) concurrent-ruby (1.3.5) + countries (8.0.4) + unaccent (~> 0.3) + country_select (11.0.0) + countries (> 6.0, < 9.0) crass (1.0.6) css_parser (1.17.1) addressable @@ -415,6 +419,7 @@ GEM concurrent-ruby (~> 1.0) uglifier (4.2.1) execjs (>= 0.3.0, < 3) + unaccent (0.4.0) warden (1.2.9) rack (>= 2.0.9) websocket (1.2.11) @@ -449,6 +454,7 @@ DEPENDENCIES ckeditor (~> 4.3.0) cocoon (~> 1.2.6) coffee-rails + country_select debug (~> 1.11) devise (~> 4.7.3) dotenv-rails diff --git a/app/controllers/facilitators_controller.rb b/app/controllers/facilitators_controller.rb index 3faf741a7..1854c0f56 100644 --- a/app/controllers/facilitators_controller.rb +++ b/app/controllers/facilitators_controller.rb @@ -13,7 +13,7 @@ def index end def show - @facilitator = Facilitator.find(params[:id]) + @facilitator = Facilitator.find(params[:id]).decorate end def new @@ -61,6 +61,6 @@ def set_facilitator # Only allow a list of trusted parameters through. def facilitator_params - params.require(:facilitator).permit(:first_name, :last_name, :email) + params.require(:facilitator).permit(Facilitator::PERMITTED_PARAMS) end end diff --git a/app/decorators/facilitator_decorator.rb b/app/decorators/facilitator_decorator.rb new file mode 100644 index 000000000..3c3bac575 --- /dev/null +++ b/app/decorators/facilitator_decorator.rb @@ -0,0 +1,24 @@ +class FacilitatorDecorator < Draper::Decorator + delegate_all + + DISPLAY_FIELDS = { + "First name" => :first_name, + "Last name" => :last_name, + "Primary email address" => :primary_email_address, + "Primary email type" => :primary_email_address_type, + "Street address" => :street_address, + "City" => :city, + "State" => :state, + "ZIP" => :zip, + "Country" => :country, + "Mailing address type" => :mailing_address_type, + "Phone number" => :phone_number, + "Phone number type" => :phone_number_type + } + + def display_fields + DISPLAY_FIELDS.map do |label, method| + { label: label, value: object.send(method) } + end + end +end diff --git a/app/models/facilitator.rb b/app/models/facilitator.rb index 5cb8899ac..d003cdf9c 100644 --- a/app/models/facilitator.rb +++ b/app/models/facilitator.rb @@ -1,7 +1,32 @@ class Facilitator < ApplicationRecord has_one :user - validates :first_name, presence: true - validates :last_name, presence: true - validates :email, presence: true + CONTACT_TYPES = ["Work", "Personal"].freeze + PERMITTED_PARAMS = [ + :first_name, :last_name, :primary_email_address, :primary_email_address_type, + :street_address, :city, :state, :zip, :country, :mailing_address_type, + :phone_number, :phone_number_type + ].freeze + + validates :first_name, + :last_name, + :primary_email_address, + :primary_email_address_type, + :street_address, + :city, + :state, + :zip, + :country, + :mailing_address_type, + :phone_number, + :phone_number_type, + presence: true + + validates :primary_email_address_type, inclusion: {in: CONTACT_TYPES} + validates :mailing_address_type, inclusion: {in: CONTACT_TYPES} + validates :phone_number_type, inclusion: {in: CONTACT_TYPES} + + # TODO: add validation for zip code containing only numbers + # TODO: add validation on STATE + # TODO: add validation on phone number type end diff --git a/app/views/facilitators/_form.html.erb b/app/views/facilitators/_form.html.erb index c628e5a63..5d2b66d6e 100644 --- a/app/views/facilitators/_form.html.erb +++ b/app/views/facilitators/_form.html.erb @@ -1,16 +1,24 @@ -<%= form_for(@facilitator) do |f| %> - <%= render 'shared/errors', resource: facilitator if facilitator.errors.any? %> - -
- <%= f.label :first_name, class: 'bold' %> - <%= f.text_field :first_name %> - <%= f.label :last_name, class: 'bold' %> - <%= f.text_field :last_name %> - <%= f.label :email, class: 'bold' %> - <%= f.text_field :email %> -
- -
- <%= f.button :submit %> -
+ <%= simple_form_for @facilitator do |f| %> + <%= f.input :first_name %> + <%= f.input :last_name %> + <%= f.input :primary_email_address %> + <%= f.input :primary_email_address_type, + as: :radio_buttons, + collection: Facilitator::CONTACT_TYPES, + item_wrapper_class: 'form-check' %> + <%= f.input :street_address %> + <%= f.input :city %> + <%= f.input :state %> + <%= f.input :zip %> + <%= f.input :country %> + <%= f.input :mailing_address_type, + as: :radio_buttons, + collection: Facilitator::CONTACT_TYPES, + item_wrapper_class: 'form-check' %> + <%= f.input :phone_number%> + <%= f.input :phone_number_type, + as: :radio_buttons, + collection: Facilitator::CONTACT_TYPES, + item_wrapper_class: 'form-check' %> + <%= f.button :submit %> <% end %> diff --git a/app/views/facilitators/index.html.erb b/app/views/facilitators/index.html.erb index ac1b05a3b..75334cd18 100644 --- a/app/views/facilitators/index.html.erb +++ b/app/views/facilitators/index.html.erb @@ -17,7 +17,7 @@ <%= facilitator.first_name %> <%= facilitator.last_name %> - <%= facilitator.email %> + <%= facilitator.primary_email_address %> <%= link_to 'Show', facilitator %> <%= link_to 'Edit', edit_facilitator_path(facilitator) %> <%= link_to 'Destroy', facilitator, method: :delete, data: { confirm: 'Are you sure?' } %> diff --git a/app/views/facilitators/show.html.erb b/app/views/facilitators/show.html.erb index e55ecff99..e0d03508c 100644 --- a/app/views/facilitators/show.html.erb +++ b/app/views/facilitators/show.html.erb @@ -1,19 +1,13 @@

<%= notice %>

-

- First name: - <%= @facilitator.first_name %> -

+<% @facilitator.display_fields.each do |field| %> +

+ <%= field[:label] %>: + <%= field[:value].presence || "N/A" %> +

+<% end %> -

- Last name: - <%= @facilitator.last_name %> -

-

- Email: - <%= @facilitator.email %> -

<%= link_to 'Edit', edit_facilitator_path(@facilitator) %> | <%= link_to 'Back', facilitators_path %> diff --git a/db/migrate/20250913164451_add_contact_info_to_facilitators.rb b/db/migrate/20250913164451_add_contact_info_to_facilitators.rb new file mode 100644 index 000000000..508a32714 --- /dev/null +++ b/db/migrate/20250913164451_add_contact_info_to_facilitators.rb @@ -0,0 +1,14 @@ +class AddContactInfoToFacilitators < ActiveRecord::Migration[6.1] + def change + add_column :facilitators, :primary_email_address, :string, null: false + add_column :facilitators, :primary_email_address_type, :string, null: false + add_column :facilitators, :street_address, :string, null: false + add_column :facilitators, :city, :string, null: false + add_column :facilitators, :state, :string, null: false + add_column :facilitators, :zip, :string, null: false + add_column :facilitators, :country, :string, null: false + add_column :facilitators, :mailing_address_type, :string, null: false + add_column :facilitators, :phone_number, :string, null: false + add_column :facilitators, :phone_number_type, :string, null: false + end +end diff --git a/db/migrate/20250913171135_remove_email_from_facilitators.rb b/db/migrate/20250913171135_remove_email_from_facilitators.rb new file mode 100644 index 000000000..c2259fac0 --- /dev/null +++ b/db/migrate/20250913171135_remove_email_from_facilitators.rb @@ -0,0 +1,5 @@ +class RemoveEmailFromFacilitators < ActiveRecord::Migration[6.1] + def change + remove_column :facilitators, :email, :string + end +end diff --git a/db/schema.rb b/db/schema.rb index 283924a3a..65178cfe6 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 2025_09_12_173152) do +ActiveRecord::Schema.define(version: 2025_09_13_171135) do create_table "admins", id: :integer, charset: "utf8mb3", force: :cascade do |t| t.string "email", default: "", null: false @@ -142,9 +142,18 @@ create_table "facilitators", charset: "utf8mb3", force: :cascade do |t| t.string "first_name", null: false t.string "last_name", null: false - t.string "email", null: false t.datetime "created_at", precision: 6, null: false t.datetime "updated_at", precision: 6, null: false + t.string "primary_email_address", null: false + t.string "primary_email_address_type", null: false + t.string "street_address", null: false + t.string "city", null: false + t.string "state", null: false + t.string "zip", null: false + t.string "country", null: false + t.string "mailing_address_type", null: false + t.string "phone_number", null: false + t.string "phone_number_type", null: false end create_table "faqs", id: :integer, charset: "utf8mb3", force: :cascade do |t| diff --git a/spec/decorators/facilitator_decorator_spec.rb b/spec/decorators/facilitator_decorator_spec.rb new file mode 100644 index 000000000..6556273f8 --- /dev/null +++ b/spec/decorators/facilitator_decorator_spec.rb @@ -0,0 +1,4 @@ +require 'rails_helper' + +RSpec.describe FacilitatorDecorator do +end diff --git a/spec/factories/facilitators.rb b/spec/factories/facilitators.rb index c52901f61..1c4241ff3 100644 --- a/spec/factories/facilitators.rb +++ b/spec/factories/facilitators.rb @@ -2,6 +2,15 @@ factory :facilitator do first_name { Faker::Name.first_name } last_name { Faker::Name.last_name } - email { Faker::Internet.unique.email } + primary_email_address { Faker::Internet.unique.email } + primary_email_address_type { 'Personal' } + street_address { Faker::Address.street_address } + city { Faker::Address.city } + state { Faker::Address.state } + zip { Faker::Address.postcode } + country { Faker::Address.country} + mailing_address_type { 'Personal' } + phone_number { Faker::PhoneNumber.phone_number } + phone_number_type { 'Personal' } end end diff --git a/spec/models/facilitator_spec.rb b/spec/models/facilitator_spec.rb index 40c41fcf5..7ac26c372 100644 --- a/spec/models/facilitator_spec.rb +++ b/spec/models/facilitator_spec.rb @@ -8,6 +8,18 @@ describe 'validations' do it { should validate_presence_of(:first_name) } it { should validate_presence_of(:last_name) } - it { should validate_presence_of(:email) } + it { should validate_presence_of(:primary_email_address) } + it { should validate_presence_of(:primary_email_address_type) } + it { should validate_presence_of(:street_address) } + it { should validate_presence_of(:city) } + it { should validate_presence_of(:state) } + it { should validate_presence_of(:zip) } + it { should validate_presence_of(:country) } + it { should validate_presence_of(:mailing_address_type) } + it { should validate_presence_of(:phone_number) } + it { should validate_presence_of(:phone_number_type) } + it { should validate_inclusion_of(:primary_email_address_type).in_array(%w[Work Personal]) } + it { should validate_inclusion_of(:mailing_address_type).in_array(%w[Work Personal]) } + it { should validate_inclusion_of(:phone_number_type).in_array(%w[Work Personal]) } end end diff --git a/spec/views/facilitators/edit.html.erb_spec.rb b/spec/views/facilitators/edit.html.erb_spec.rb index 9259818aa..46d5026de 100644 --- a/spec/views/facilitators/edit.html.erb_spec.rb +++ b/spec/views/facilitators/edit.html.erb_spec.rb @@ -15,7 +15,7 @@ it "has a form with the facilitator fields" do expect(rendered).to have_field('First name', with: facilitator.first_name) expect(rendered).to have_field('Last name', with: facilitator.last_name) - expect(rendered).to have_field('Email', with: facilitator.email) + expect(rendered).to have_field('Primary email address', with: facilitator.primary_email_address) end it "has a link to the show page" do diff --git a/spec/views/facilitators/index.html.erb_spec.rb b/spec/views/facilitators/index.html.erb_spec.rb index 11d2dac10..274617b9f 100644 --- a/spec/views/facilitators/index.html.erb_spec.rb +++ b/spec/views/facilitators/index.html.erb_spec.rb @@ -10,7 +10,7 @@ end it "renders a list of facilitators" do - expect(rendered).to match(facilitator.email) - expect(rendered).to match(facilitator_2.email) + expect(rendered).to match(facilitator.primary_email_address) + expect(rendered).to match(facilitator_2.primary_email_address) end end diff --git a/spec/views/facilitators/new.html.erb_spec.rb b/spec/views/facilitators/new.html.erb_spec.rb index 304cc903f..0f8c3be5b 100644 --- a/spec/views/facilitators/new.html.erb_spec.rb +++ b/spec/views/facilitators/new.html.erb_spec.rb @@ -15,7 +15,7 @@ it "has a form with the facilitator fields" do expect(rendered).to have_field('First name') expect(rendered).to have_field('Last name') - expect(rendered).to have_field('Email') + expect(rendered).to have_field('Primary email address') end it "has a link back to the index page" do diff --git a/spec/views/facilitators/show.html.erb_spec.rb b/spec/views/facilitators/show.html.erb_spec.rb index db286e47f..1b5c2bc54 100644 --- a/spec/views/facilitators/show.html.erb_spec.rb +++ b/spec/views/facilitators/show.html.erb_spec.rb @@ -4,13 +4,13 @@ let(:facilitator) { create(:facilitator)} before do - assign(:facilitator, facilitator) + assign(:facilitator, facilitator.decorate) render end it "renders attributes in

" do expect(rendered).to match(facilitator.first_name) expect(rendered).to match(facilitator.last_name) - expect(rendered).to match(facilitator.email) + expect(rendered).to match(facilitator.primary_email_address) end end