Skip to content

Replace Chosen.js with TomSelect for meeting invitations#2566

Open
mroderick wants to merge 1 commit intocodebar:masterfrom
mroderick:feature/tom-select-meeting-invitations
Open

Replace Chosen.js with TomSelect for meeting invitations#2566
mroderick wants to merge 1 commit intocodebar:masterfrom
mroderick:feature/tom-select-meeting-invitations

Conversation

@mroderick
Copy link
Copy Markdown
Collaborator

@mroderick mroderick commented Apr 11, 2026

Why

The /admin/meetings/:id invitation management page loads all members into a dropdown using Member.all, identical to the anti-pattern fixed in PR #2562:

  • Loads ~28,000 members on every page render
  • Causes slow page loads
  • Will only get worse as membership grows
  • Chosen.js is unmaintained (archived since 2017)

What

Replace Chosen.js with TomSelect for the member lookup field in meeting invitations, using remote data loading:

  • TomSelect fetches members on-demand via the existing /admin/members/search endpoint
  • Minimum 3 characters before search triggers
  • 300ms debounce on requests
  • Searches both name and email fields
  • Results limited to 50 matches

Changes

Frontend:

  • Updated app/views/admin/meetings/_invitation_management.html.haml - removed Member.all, changed class to tom-select
  • Updated app/views/admin/meetings/show.html.haml - added TomSelect CDN via content_for :head

JavaScript:

  • Added TomSelect initialization for #meeting_invitations_member in app/assets/javascripts/application.js
  • Excluded #meeting_invitations_member from Chosen.js initialization

Tests:

  • Created spec/support/select_from_tom_select.rb - Capybara helper for TomSelect dropdowns
  • Updated spec/features/admin/managing_meeting_invitations_spec.rb - changed from select to select_from_tom_select, added :js metadata

How to Verify Locally

1. Run migrations and seed (if needed)

bundle exec rails db:migrate db:seed

2. Start the server

bundle exec rails server

3. Ensure you have an admin user

bundle exec rails runner "Member.find_by(email: 'your-email@example.com').add_role(:admin)"

Or log in via GitHub OAuth and visit /admin/members to verify admin access.

4. Create test data (if not exists)

bundle exec rails runner "
m = Meeting.first || Fabricate(:meeting, name: 'Test Meeting')
members = Member.limit(3).to_a
members.each { |member| MeetingInvitation.create!(meeting: m, member: member, attending: true, role: 'Participant') rescue nil }
puts 'Meeting URL: /admin/meetings/' + m.slug
"

5. Test the member search

  1. Visit /admin/meetings/:slug (replace :slug with the meeting slug from step 4)
  2. The invitation management section appears at the bottom (requires at least 1 invitation)
  3. Click the member dropdown - it should show "Type to search members..." placeholder
  4. Type 2 characters - no search should trigger (minimum 3 required)
  5. Type 3+ characters - search triggers with 300ms debounce
  6. Results show member name and email
  7. Select a member - the field updates
  8. Click "Add" - the invitation is created
image

6. Run the tests

bundle exec rspec spec/features/admin/managing_meeting_invitations_spec.rb

All 3 tests should pass:

  • creating a new meeting invitation for a member that is not already attending (JS)
  • creating a new meeting invitation for a member that is already attending (JS)
  • Updating the attendance of an invitation

7. Verify TomSelect tests still work

bundle exec rspec spec/features/admin/tom_select_member_lookup_spec.rb

Both tests should pass (the /admin/members page TomSelect functionality).

Related

Future Work

Remaining Member.all scalability issues:

  1. Meeting form organisers field - app/views/admin/meetings/_form.html.haml line 20 uses Member.all for the organisers multi-select (same issue, different page)

Other Chosen.js uses (small datasets, low priority):

  • Workshop invitation management - Uses Chosen.js with @workshop.invitations.not_accepted (local data, ~dozens of records)
  • Feedback form - Uses Chosen.js with @coaches (workshop's coaches, ~5-20) and Tutorial.all (small dataset)
  • Sponsors chapter filter - Uses Chosen.js with @chapters (small dataset, ~10-20)
  • invitations.js - AJAX-loaded content for workshop invitations

Final cleanup:

  • Remove Chosen.js gem and vendor assets once all uses are replaced

- Replace Chosen.js dropdown with TomSelect for member lookup
- Load members dynamically via /admin/members/search endpoint
- Add select_from_tom_select helper for Capybara tests
- Update tests for remote data loading
- Exclude #meeting_invitations_member from Chosen.js initialization

Reuses /admin/members/search endpoint from PR codebar#2562.
Fixes performance issue loading 28k+ members on every page render.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant