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
1 change: 1 addition & 0 deletions app/assets/javascripts/application.js
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
//= require dietary-restrictions
//= require cocoon
//= require font_awesome5
//= require how-you-found-us

$(function() {
$("body").removeClass("no-js");
Expand Down
20 changes: 20 additions & 0 deletions app/assets/javascripts/how-you-found-us.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
$(document).ready(function() {
const $otherRadioButton = $('#member_how_you_found_us_other');
const $otherReason = $('#member_how_you_found_us_other_reason');
const $elementToToggle = $otherReason.parent();

function toggleOtherReason() {
if ($otherRadioButton.is(':checked')) {
$elementToToggle.removeClass('d-none').hide().slideDown(50);
$otherReason.prop('disabled', false).focus(); // Optional — disabling is not needed
} else {
$elementToToggle.slideUp(50, function() {
$elementToToggle.addClass('d-none');
$otherReason.val('');
});
}
}

toggleOtherReason();
$('input[name="member[how_you_found_us]"]').on('change', toggleOtherReason);
});
13 changes: 11 additions & 2 deletions app/controllers/concerns/member_concerns.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,8 @@ module InstanceMethods

def member_params
params.require(:member).permit(
:pronouns, :name, :surname, :email, :mobile, :about_you, :skill_list, :newsletter, :other_dietary_restrictions,
dietary_restrictions: [],
:pronouns, :name, :surname, :email, :mobile, :about_you, :skill_list, :newsletter, :other_dietary_restrictions, :how_you_found_us,
:how_you_found_us_other_reason, dietary_restrictions: []
).tap do |params|
# We want to keep Rails' hidden blank field in the form so that all dietary restrictions for a member can be
# removed by submitting the form with all check boxes unticked. However, we want to remove the blank value
Expand All @@ -29,5 +29,14 @@ def suppress_notices
def set_member
@member = current_user
end

def how_you_found_us_selections_valid?
how_found_present = member_params[:how_you_found_us].present?
other_reason_present = member_params[:how_you_found_us_other_reason].present?
return false if member_params[:how_you_found_us] == 'other' && !other_reason_present
return true if member_params[:how_you_found_us] == 'other' && other_reason_present

how_found_present != other_reason_present
end
end
end
17 changes: 15 additions & 2 deletions app/controllers/member/details_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,26 @@ class Member::DetailsController < ApplicationController

def edit
accept_terms

flash[notice] = I18n.t('notifications.signing_up')
@member.newsletter ||= true
end

def update
return render :edit unless @member.update(member_params)
attrs = member_params
attrs[:how_you_found_us_other_reason] = nil if attrs[:how_you_found_us] != 'other'

unless how_you_found_us_selections_valid?
@member.errors.add(:how_you_found_us, 'You must select one option')
return render :edit
end
attrs[:how_you_found_us] = params[:member][:how_you_found_us] if params[:member][:how_you_found_us].present?

if params[:member][:how_you_found_us_other_reason].present? && attrs[:how_you_found_us] == 'other'
attrs[:how_you_found_us_other_reason] =
params[:member][:how_you_found_us_other_reason]
end

return render :edit unless @member.update(attrs)

member_params[:newsletter] ? subscribe_to_newsletter(@member) : unsubscribe_from_newsletter(@member)
redirect_to step2_member_path
Expand Down
8 changes: 8 additions & 0 deletions app/models/member.rb
Original file line number Diff line number Diff line change
@@ -1,6 +1,14 @@
class Member < ApplicationRecord
include Permissions

enum how_you_found_us: {
Copy link
Contributor

Choose a reason for hiding this comment

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

Thanks for making this change ❤️

from_a_friend: 0,
search_engine: 1,
social_media: 2,
codebar_host_or_partner: 3,
other: 4
}

has_many :attendance_warnings
has_many :bans
has_many :eligibility_inquiries
Expand Down
24 changes: 24 additions & 0 deletions app/views/member/details/edit.html.haml
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,30 @@
label_method: ->(r) { r.humanize.upcase_first }
= f.input :other_dietary_restrictions, placeholder: 'Other dietary restrictions',
wrapper_html: { class: class_names('mt-n3', 'd-none': [email protected]_dietary_restrictions?) }, label_html: { class: 'sr-only' }
- if @member.errors.any?
#error_explanation
%h2= "#{pluralize(@member.errors.count, 'error')} prohibited this member from being saved:"
%ul
- @member.errors.full_messages.each do |msg|
%li= msg
- if @member.errors[:how_you_found_us]&.any?
Copy link
Contributor

@mikej mikej Sep 9, 2025

Choose a reason for hiding this comment

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

I think this should be simplifiable into two f.input lines: one for the checkboxes and one for the "other" text field.

Simple Form can take care of displaying the labels for each check box value and for displaying any validation errors for the fields.

Simple Form detects that an attribute on the model is an array so you should just need to provide the collection of allowed values and the indication of whether to use check boxes vs. radio buttons.

Something like:

f.input :how_you_found_us, collection: <a constant for the list of options>, as: :check_boxes

Copy link
Collaborator

Choose a reason for hiding this comment

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

(Typo: inout -> input.)

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I've implemented this for the most part.

%span.text-danger= @member.errors[:how_you_found_us].first
.col-12.mb-3
%fieldset
= f.input :how_you_found_us,
as: :radio_buttons,
collection: Member.how_you_found_us.keys,
label_method: ->(option) { t("member.details.edit.how_you_found_us_options.#{option}") },
value_method: :to_s,
label: t('member.details.edit.how_you_found_us_label'),
item_wrapper_class: 'form-check d-flex align-items-center mb-2',
label_item: true,
input_html: { class: 'form-check-input me-2', style: 'margin-top: 0;' },
label_html: { class: 'form-check-label', style: 'margin-left: 0;' }

= f.input :how_you_found_us_other_reason,
label: t('member.details.edit.how_you_found_us_other_reason'),
input_html: { class: 'form-control w-100' }
= f.input :newsletter, as: :boolean, checked_value: true, unchecked_value: false
.text-right.mb-4
= hidden_field_tag :next_page, step2_member_path(member_type: @type)
Expand Down
8 changes: 8 additions & 0 deletions config/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -442,6 +442,14 @@ en:
edit:
title: Almost there...
summary: We need some more details from you to finish creating your account. We use these to help run our events.
how_you_found_us_label: "How did you find out about us?*"
how_you_found_us_other_reason: "Please specify how you found us"
how_you_found_us_options:
from_a_friend: "From a friend"
search_engine: "Search engine (Google etc.)"
social_media: "Social media"
codebar_host_or_partner: "One of Codebar's hosts or partners"
other: "Other"
coach:
about_you: What experience do you have? What languages do you like to use? Tell us a little bit about yourself!
student:
Expand Down
6 changes: 6 additions & 0 deletions db/migrate/20250823151717_add_how_you_found_us_options.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
class AddHowYouFoundUsOptions < ActiveRecord::Migration[7.0]
def change
add_column :members, :how_you_found_us, :integer
add_column :members, :how_you_found_us_other_reason, :string
end
end
4 changes: 3 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[7.1].define(version: 2025_08_20_145012) do
ActiveRecord::Schema[7.1].define(version: 2025_08_23_151717) do
# These are extensions that must be enabled in order to support this database
enable_extension "plpgsql"

Expand Down Expand Up @@ -397,6 +397,8 @@
t.datetime "opt_in_newsletter_at", precision: nil
t.enum "dietary_restrictions", default: [], array: true, enum_type: "dietary_restriction_enum"
t.string "other_dietary_restrictions"
t.integer "how_you_found_us"
t.string "how_you_found_us_other_reason"
t.index ["email"], name: "index_members_on_email", unique: true
end

Expand Down
102 changes: 102 additions & 0 deletions spec/controllers/member/details_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
RSpec.describe Member::DetailsController do
render_views
let(:member) { Fabricate(:member) }

before do
allow(controller).to receive(:current_user).and_return(member)
allow_any_instance_of(Services::MailingList).to receive(:subscribe).and_return(true)
end

describe 'PATCH #update' do
context 'with valid params' do
it 'updates how_you_found_us with radio option' do
patch :update, params: {
id: member.id,
member: {
how_you_found_us: 'social_media',
newsletter: 'true'
}
}

member.reload
expect(I18n.t("member.details.edit.how_you_found_us_options.#{member.how_you_found_us}")).to eq('Social media')
expect(member.how_you_found_us_other_reason).to eq(nil)
expect(response).to redirect_to(step2_member_path)
end

it 'adds other_reason to how_you_found_us when provided' do
patch :update, params: {
id: member.id,
member: {
how_you_found_us: 'other',
how_you_found_us_other_reason: 'Saw a pamphlet',
newsletter: 'false'
},
}

member.reload
expect(member.how_you_found_us).to eq('other')
expect(member.how_you_found_us_other_reason).to eq('Saw a pamphlet')
expect(response).to redirect_to(step2_member_path)
end

it 'updates how_you_found_us with only other_reason' do
patch :update, params: {
id: member.id,
member: {
how_you_found_us: 'other',
how_you_found_us_other_reason: 'At a meetup',
newsletter: 'true'
},
}

member.reload
expect(member.how_you_found_us).to eq('other')
expect(member.how_you_found_us_other_reason).to eq('At a meetup')
expect(response).to redirect_to(step2_member_path)
end

it 'removes duplicates and blank entries' do
patch :update, params: {
id: member.id,
member: {
how_you_found_us: 'other',
how_you_found_us_other_reason: 'From a colleague',
newsletter: 'true'
},
}

member.reload
expect(member.how_you_found_us).to eq('other')
expect(member.how_you_found_us_other_reason).to eq('From a colleague')
expect(response).to redirect_to(step2_member_path)
end
end

context 'when update fails (invalid data)' do
it 'error raised when no how you found us selection given' do
patch :update, params: {
id: member.id,
member: {
how_you_found_us: 'other',
how_you_found_us_other_reason: nil,
}
}

expect(response.body).to include('You must select one option')
end

it 'error raised when both how you found us fields popoulated' do
patch :update, params: {
id: member.id,
member: {
how_you_found_us: 'from_a_friend',
how_you_found_us_other_reason: 'something else',
}
}

expect(response.body).to include('You must select one option')
end
end
end
end
8 changes: 7 additions & 1 deletion spec/features/member_joining_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
end

scenario 'A visitor must fill in all mandatory fields in order to sign up' do
member = Fabricate(:member, name: nil, surname: nil, email: nil, about_you: nil)
member = Fabricate(:member, name: nil, surname: nil, email: nil, about_you: nil, how_you_found_us: nil, how_you_found_us_other_reason: nil)
member.update(can_log_in: true)
login member

Expand All @@ -27,6 +27,7 @@
expect(page).to have_content "Surname can't be blank"
expect(page).to have_content "Email address can't be blank"
expect(page).to have_content "About you can't be blank"
expect(page).to have_content "You must select one option"
end

scenario 'A new member details are successfully captured' do
Expand All @@ -43,6 +44,11 @@
check 'Vegan'
check 'Other'
fill_in 'Other dietary restrictions', with: 'peanut allergy'
find('#member_how_you_found_us_from_a_friend').click

find('#member_how_you_found_us_other').click
expect(page).to have_content('Please specify how you found us')
fill_in 'member_how_you_found_us_other_reason', with: 'found on a poster', id: true
click_on 'Next'

expect(page).to have_current_path(step2_member_path)
Expand Down
3 changes: 3 additions & 0 deletions spec/features/subscribing_to_newsletter_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@
fill_in 'member_surname', with: 'Doe'
fill_in 'member_email', with: '[email protected]'
fill_in 'member_about_you', with: Faker::Lorem.paragraph
find('#member_how_you_found_us_from_a_friend').click

click_on 'Next'
end
Expand All @@ -46,6 +47,8 @@
fill_in 'member_surname', with: 'Doe'
fill_in 'member_email', with: '[email protected]'
fill_in 'member_about_you', with: Faker::Lorem.paragraph
find('#member_how_you_found_us_other').click
fill_in 'member_how_you_found_us_other_reason', with: Faker::Lorem.paragraph, id: true

uncheck 'newsletter'

Expand Down