diff --git a/app/components/chapter_picker_component.html.erb b/app/components/chapter_picker_component.html.erb index 7d30afa31..eabbc20cf 100644 --- a/app/components/chapter_picker_component.html.erb +++ b/app/components/chapter_picker_component.html.erb @@ -2,6 +2,7 @@ list: datalist_id, placeholder: @placeholder, class: 'form-control', + required: @required, autocomplete: 'off' %> <%= tag.datalist id: datalist_id do %> diff --git a/app/components/chapter_picker_component.rb b/app/components/chapter_picker_component.rb index c74c52d79..ddb8eef71 100644 --- a/app/components/chapter_picker_component.rb +++ b/app/components/chapter_picker_component.rb @@ -1,12 +1,13 @@ # frozen_string_literal: true class ChapterPickerComponent < ViewComponent::Base - def initialize(name:, chapters:, selected: nil, placeholder: 'Select a chapter') + def initialize(name:, chapters:, selected: nil, placeholder: 'Select a chapter', required: false) super() @name = name @chapters = chapters @selected = selected @placeholder = placeholder + @required = required end def datalist_id diff --git a/app/models/workshop.rb b/app/models/workshop.rb index 6c47b5ae8..02064f707 100644 --- a/app/models/workshop.rb +++ b/app/models/workshop.rb @@ -35,9 +35,9 @@ class Workshop < ApplicationRecord validates :student_spaces, numericality: { greater_than: 0 }, if: :virtual? validates :coach_spaces, numericality: { greater_than: 0 }, if: :virtual? - before_validation :set_date_and_time, :set_end_date_and_time, if: proc { |model| model.chapter_id.present? } - before_validation :set_opens_at - before_validation :set_closes_at + before_validation :set_date_and_time, :set_end_date_and_time, + :set_opens_at, :set_closes_at, + if: proc { |model| model.chapter_id.present? } validate :rsvp_date_time_fields_must_be_paired validate :rsvp_close_before_workshop_start diff --git a/app/views/admin/workshops/_shared_form.html.haml b/app/views/admin/workshops/_shared_form.html.haml index 02029483c..86729801d 100644 --- a/app/views/admin/workshops/_shared_form.html.haml +++ b/app/views/admin/workshops/_shared_form.html.haml @@ -3,13 +3,13 @@ = f.hidden_field :chapter_id - else .col-12 - = render(ChapterPickerComponent.new(name: 'workshop[chapter_id]', chapters: Chapter.available_to_user(current_user), placeholder: 'Select a chapter')) + = render(ChapterPickerComponent.new(name: 'workshop[chapter_id]', chapters: Chapter.available_to_user(current_user), placeholder: 'Select a chapter', required: true)) .col-12 - = f.input :local_date, label: 'Date', required: true, input_html: { value: @workshop.date_and_time.try(:strftime, '%Y-%m-%d'), type: :date } + = f.input :local_date, label: 'Date', required: true, input_html: { value: @workshop.local_date.presence || @workshop.date_and_time.try(:strftime, '%Y-%m-%d'), type: :date } .col-12.col-md-6 - = f.input :local_time, label: 'Begins at', required: true, input_html: { value: @workshop.time.try(:strftime, '%H:%M'), type: :time } + = f.input :local_time, label: 'Begins at', required: true, input_html: { value: @workshop.local_time.presence || @workshop.time.try(:strftime, '%H:%M'), type: :time } .col-12.col-md-6 - = f.input :local_end_time, label: 'Ends at', required: true, input_html: { value: @workshop.ends_at.try(:strftime, '%H:%M'), type: :time } + = f.input :local_end_time, label: 'Ends at', required: true, input_html: { value: @workshop.local_end_time.presence || @workshop.ends_at.try(:strftime, '%H:%M'), type: :time } .col-12#host = f.input :host, as: :select, collection: Sponsor.all, include_blank: true, selected: (@workshop.host.id rescue '') .col-12 @@ -27,11 +27,11 @@ %p.small.text-muted= t('admin.workshop.form.rsvp_details') .row .col-12.col-md-6 - = f.input :rsvp_open_local_date, label: 'Open date', input_html: { value: @workshop.rsvp_opens_at.try(:strftime, '%Y-%m-%d'), type: :date } + = f.input :rsvp_open_local_date, label: 'Open date', input_html: { value: @workshop.rsvp_open_local_date.presence || @workshop.rsvp_opens_at.try(:strftime, '%Y-%m-%d'), type: :date } .col-12.col-md-6 - = f.input :rsvp_open_local_time, label: 'Open time', input_html: { value: @workshop.rsvp_opens_at.try(:time).try(:strftime, '%H:%M'), type: :time } + = f.input :rsvp_open_local_time, label: 'Open time', input_html: { value: @workshop.rsvp_open_local_time.presence || @workshop.rsvp_opens_at.try(:time).try(:strftime, '%H:%M'), type: :time } .row .col-12.col-md-6 - = f.input :rsvp_close_local_date, label: 'Close date', input_html: { value: @workshop.rsvp_closes_at.try(:strftime, '%Y-%m-%d'), type: :date } + = f.input :rsvp_close_local_date, label: 'Close date', input_html: { value: @workshop.rsvp_close_local_date.presence || @workshop.rsvp_closes_at.try(:strftime, '%Y-%m-%d'), type: :date } .col-12.col-md-6 - = f.input :rsvp_close_local_time, label: 'Close time', input_html: { value: @workshop.rsvp_closes_at.try(:time).try(:strftime, '%H:%M'), type: :time } + = f.input :rsvp_close_local_time, label: 'Close time', input_html: { value: @workshop.rsvp_close_local_time.presence || @workshop.rsvp_closes_at.try(:time).try(:strftime, '%H:%M'), type: :time } diff --git a/spec/controllers/admin/workshops_controller_spec.rb b/spec/controllers/admin/workshops_controller_spec.rb index 1f2db20ac..990a27426 100644 --- a/spec/controllers/admin/workshops_controller_spec.rb +++ b/spec/controllers/admin/workshops_controller_spec.rb @@ -162,6 +162,8 @@ def assigns(symbol) end describe 'POST #create' do + let(:sponsor) { Fabricate(:sponsor) } + it 'permits rsvp_close_local_date and rsvp_close_local_time' do expect do post :create, params: { workshop: { rsvp_close_local_date: '01/12/2020', rsvp_close_local_time: '15:00', host: '' } } @@ -187,6 +189,36 @@ def assigns(symbol) expect(PublicActivity::Activity.exists?(owner: admin, key: 'workshop.created', trackable: created)).to be(true) end + + context 'when no chapter is selected' do + render_views + + def create_workshop + post :create, params: { + workshop: { + chapter_id: '', local_date: '2026-09-21', local_time: '18:00', + local_end_time: '22:00', host: sponsor.id.to_s, + sponsor_ids: ['', sponsor.id.to_s], description: '', + rsvp_open_local_date: '', rsvp_open_local_time: '', + rsvp_close_local_date: '2026-09-21', rsvp_close_local_time: '14:30', + virtual: '0', coach_spaces: '0', student_spaces: '0', invitable: '1' + } + } + end + + it 'renders the form with a validation warning instead of raising' do + expect { create_workshop }.not_to raise_error + expect(response).to have_http_status(:ok) + expect(flash[:warning]).to include("Chapter can't be blank") + end + + it 'keeps the submitted RSVP close values in the form' do + create_workshop + + expect(response.body).to include('value="2026-09-21"') + expect(response.body).to include('value="14:30"') + end + end end describe 'DELETE #destroy' do