diff --git a/app/controllers/admin/forms_controller.rb b/app/controllers/admin/forms_controller.rb index 174de93db..446cd0a2a 100644 --- a/app/controllers/admin/forms_controller.rb +++ b/app/controllers/admin/forms_controller.rb @@ -150,6 +150,7 @@ def update_admin_options def update_form_manager_options ensure_form_manager(form: @form) @form.update(form_admin_options_params) + set_service_stage_options flash.now[:notice] = 'Form Manager forms options updated successfully' end @@ -158,6 +159,7 @@ def show format.html do ensure_response_viewer(form: @form) unless @form.template? @questions = @form.ordered_questions + set_service_stage_options @events = @events = Event.where(object_type: 'Form', object_uuid: @form.uuid).order("created_at DESC") end @@ -517,6 +519,7 @@ def form_params :success_text, :success_text_heading, :service_id, + :service_stage_id, # PRA Info :omb_approval_number, :expiration_date, @@ -588,10 +591,15 @@ def form_admin_options_params :omb_approval_number, :expiration_date, :service_id, + :service_stage_id, :enforce_new_submission_validations, ) end + def set_service_stage_options + @service_stage_options = @form.service.present? ? @form.service.service_stages.order(:position, :name).map { |s| [s.name, s.id] } : [] + end + # Add rules for AASM state transitions here def transition_state Event.log_event(Event.names[:form_published], 'Form', @form.uuid, "Form #{@form.name} published at #{DateTime.now}", current_user.id) if (params['form']['aasm_state'] == 'published') && !@form.published? diff --git a/app/controllers/admin/service_stages_controller.rb b/app/controllers/admin/service_stages_controller.rb index 934aef207..a840acc32 100644 --- a/app/controllers/admin/service_stages_controller.rb +++ b/app/controllers/admin/service_stages_controller.rb @@ -13,7 +13,14 @@ def index else ensure_service_manager_permissions @service_stages = ServiceStage.order(:position) - render :unscoped_index + end + respond_to do |format| + format.html { + render :index + } + format.json { + render json: @service_stages + } end end diff --git a/app/views/admin/forms/_form_manager_options.html.erb b/app/views/admin/forms/_form_manager_options.html.erb index bb2f5e645..db6b91ac0 100644 --- a/app/views/admin/forms/_form_manager_options.html.erb +++ b/app/views/admin/forms/_form_manager_options.html.erb @@ -49,6 +49,16 @@ <%= f.select :service_id, form.organization.services.includes(:organization).order("organizations.name", :name).map { |h| ["#{h.organization.name} - #{h.name}", h.id] }, { include_blank: true }, class: "usa-select" %> +
+ <%= f.label :service_stage_id, "Service Stage", class: "usa-label" %> + + Select a Service in order to specify a Service Stage. + <%= link_to "Service Stage", admin_services_path, target: "_blank" %>. + + + <%= f.select :service_stage_id, options_for_select(service_stage_options, selected: form.try(:service_stage_id)), { include_blank: true }, class: "usa-select" %> +
+
<%= f.label :audience, nil, class: "usa-label" %> @@ -129,3 +139,34 @@
<% end %> <% end %> + + \ No newline at end of file diff --git a/app/views/admin/forms/show.html.erb b/app/views/admin/forms/show.html.erb index 79d74d106..636a3c6e8 100644 --- a/app/views/admin/forms/show.html.erb +++ b/app/views/admin/forms/show.html.erb @@ -65,13 +65,6 @@
-
-
-
- Form information -
-
-
@@ -226,7 +219,7 @@ <%= render 'admin/forms/admin_options', form: @form %>
- <%= render 'admin/forms/form_manager_options', form: @form %> + <%= render 'admin/forms/form_manager_options', form: @form, service_stage_options: @service_stage_options %>
<% end %>
diff --git a/app/views/admin/forms/update_form_manager_options.js.erb b/app/views/admin/forms/update_form_manager_options.js.erb index d7fbd7008..8ec00cd89 100644 --- a/app/views/admin/forms/update_form_manager_options.js.erb +++ b/app/views/admin/forms/update_form_manager_options.js.erb @@ -1 +1 @@ -$(".form-manager-options").html("<%= j(render('admin/forms/form_manager_options', form: @form )) %>"); \ No newline at end of file +$(".form-manager-options").html("<%= j(render('admin/forms/form_manager_options', form: @form, service_stage_options: @service_stage_options )) %>"); \ No newline at end of file diff --git a/app/views/admin/submissions/index.html.erb b/app/views/admin/submissions/index.html.erb index 6ecb7db44..efed2191f 100644 --- a/app/views/admin/submissions/index.html.erb +++ b/app/views/admin/submissions/index.html.erb @@ -2,90 +2,94 @@
<%= render 'admin/submissions/submissions', submissions: submissions, form: form %>
-
+ +
Export responses
-
- <%= form_with(url: export_admin_form_path(@form), class: "usa-form", method: :get, local: true) do %> -
- - - - -
-
- -
- <% end %> -
- <%- if form.kind == "a11_v2" %> -
- - A-11 v2 Reporting - -
-
-
- Export A11-v2 Responses -
- <%= form_with(url: export_a11_v2_submissions_admin_form_url(@form, start_date: "2019-10-01"), class: "usa-form", method: :get, local: true) do %> -
- - - - -
-
- +
+
+ +
mm/dd/yyyy
+
+
- <% end %> +
-
-
- Export Form and A11-v2 Responses +
+ +
mm/dd/yyyy
+
+
- <%= form_with(url: export_form_and_a11_v2_submissions_admin_form_url(@form, start_date: "2019-10-01"), class: "usa-form", method: :get, local: true) do %> -
- - +
- - -
-
- -
- <% end %> +
+ +
+ <%- if form.kind == "a11_v2" %> +
+ +
for A-11 reporting
- <% end -%> +
+ +
to see form and A-11 responses together
+
+ <% end %> + <% else %>
@@ -108,24 +112,6 @@ } } - function updateExportLink() { - const selectedOption = document.getElementById('fiscal_year').selectedOptions[0]; - document.getElementById('start_date').value = selectedOption.getAttribute('data-start'); - document.getElementById('end_date').value = selectedOption.getAttribute('data-end'); - } - - function updateA11ExportLink() { - const selectedOption = document.getElementById('a11-fiscal-year').selectedOptions[0]; - document.getElementById('a11_start_date').value = selectedOption.getAttribute('data-start'); - document.getElementById('a11_end_date').value = selectedOption.getAttribute('data-end'); - } - - function updateA11ExportLink() { - const selectedOption = document.getElementById('form-and-a11-fiscal-year').selectedOptions[0]; - document.getElementById('form_and_a11_start_date').value = selectedOption.getAttribute('data-start'); - document.getElementById('form_and_a11_end_date').value = selectedOption.getAttribute('data-end'); - } - // When mousing over a cell with long text, show it all // When mousing out, display 50 characters max $(function() { @@ -136,5 +122,34 @@ $(".truncate").on("mouseout", function() { $(this).text(truncate($(this).attr("fulltext"), 50)); }); + + const startDateInput = document.getElementById('start-date'); + const endDateInput = document.getElementById('end-date'); + const buttons = document.querySelectorAll('form#export-buttons button'); + + function toggleButtons() { + const startDateValue = new Date(startDateInput.value); + const endDateValue = new Date(endDateInput.value); + + let isValid = true; + + if (!startDateInput.value || !endDateInput.value) { + isValid = false; // Both dates must be filled + } else if (endDateValue <= startDateValue) { + isValid = false; // End date must be greater than start date + } + + buttons.forEach((button) => { + button.disabled = !isValid; + }); + } + + if (startDateInput && endDateInput) { + // Add event listeners to both date inputs + startDateInput.addEventListener('input', toggleButtons); + endDateInput.addEventListener('input', toggleButtons); + toggleButtons() + } + }); diff --git a/app/views/components/forms/question_types/_radio_buttons.html.erb b/app/views/components/forms/question_types/_radio_buttons.html.erb index 1c6656c1f..463a74f1c 100644 --- a/app/views/components/forms/question_types/_radio_buttons.html.erb +++ b/app/views/components/forms/question_types/_radio_buttons.html.erb @@ -1,4 +1,4 @@ -
+
<%= render 'components/question_title_legend', question: question %>
<% question.question_options.each_with_index do |option, index| %> diff --git a/db/migrate/20250115175322_add_service_stage_to_forms.rb b/db/migrate/20250115175322_add_service_stage_to_forms.rb new file mode 100644 index 000000000..b1dec6071 --- /dev/null +++ b/db/migrate/20250115175322_add_service_stage_to_forms.rb @@ -0,0 +1,5 @@ +class AddServiceStageToForms < ActiveRecord::Migration[7.2] + def change + add_column :forms, :service_stage_id, :integer, default: nil + end +end diff --git a/db/schema.rb b/db/schema.rb index bf93a37d2..8340e667d 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[7.2].define(version: 2025_01_10_215441) do +ActiveRecord::Schema[7.2].define(version: 2025_01_15_175322) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -310,6 +310,7 @@ t.string "audience", default: "public", comment: "indicates whether a form is intended for a public or internal audience" t.string "short_uuid", limit: 8 t.boolean "enforce_new_submission_validations", default: true + t.integer "service_stage_id" t.index ["legacy_touchpoint_id"], name: "index_forms_on_legacy_touchpoint_id" t.index ["legacy_touchpoint_uuid"], name: "index_forms_on_legacy_touchpoint_uuid" t.index ["organization_id"], name: "index_forms_on_organization_id" diff --git a/lib/fiscal_year.rb b/lib/fiscal_year.rb index d66f441fb..25bb8f6d5 100644 --- a/lib/fiscal_year.rb +++ b/lib/fiscal_year.rb @@ -1,4 +1,21 @@ module FiscalYear + def self.first_day_of_fiscal_quarter(date) + # Adjust the month to align with the fiscal year starting in October + fiscal_month = (date.month - 10) % 12 + 1 + fiscal_quarter = ((fiscal_month - 1) / 3) + 1 + + # Calculate the first month of the fiscal quarter + fiscal_start_month = ((fiscal_quarter - 1) * 3 + 10) % 12 + fiscal_start_month = 12 if fiscal_start_month == 0 # Handle December correctly + + # Determine the fiscal year for the quarter + fiscal_year = date.year + fiscal_year -= 1 if date.month < 10 + + # Return the first day of the quarter + Date.new(fiscal_year, fiscal_start_month, 1) + end + def self.last_day_of_fiscal_quarter(fiscal_year, fiscal_quarter) year = fiscal_quarter == 1 ? fiscal_year - 1 : fiscal_year month = case fiscal_quarter diff --git a/spec/features/admin/forms_spec.rb b/spec/features/admin/forms_spec.rb index e3ef9f519..49e1ec453 100644 --- a/spec/features/admin/forms_spec.rb +++ b/spec/features/admin/forms_spec.rb @@ -319,7 +319,7 @@ it "display 'Submitted' flash message" do expect(page).to have_content("Viewing Form: #{form.name}") - expect(page).to have_content('Form Information'.upcase) + expect(page).to have_link("Back to Forms") expect(page).to have_content('This form has been Submitted successfully.') end end @@ -350,7 +350,7 @@ it "display 'Published' flash message" do expect(page).to have_content('Published') expect(page).to have_content("Viewing Form: #{form.name}") - expect(page).to have_content('Form Information'.upcase) + expect(page).to have_link("Back to Forms") end end end @@ -369,7 +369,7 @@ it "display 'Submitted' flash message" do expect(page).to have_content("Viewing Form: #{form.name}") - expect(page).to have_content('Form Information'.upcase) + expect(page).to have_link("Back to Forms") expect(page).to have_content('This form has been Submitted successfully.') end end @@ -526,7 +526,7 @@ within('table.submissions') do expect(page).to have_content(submission.answer_01) end - expect_responses_fiscal_year_dropdown + expect_start_and_end_date_fields end end end @@ -546,7 +546,7 @@ expect(page).to have_content('RESPONSES BY STATUS') expect(page).to have_content('Responses per day') expect(page).to have_content('Total submissions received over period') - expect_responses_fiscal_year_dropdown + expect_start_and_end_date_fields expect(page).to have_content('Performance.gov Reporting') expect(page).to have_content('Responses Summary') end @@ -1731,11 +1731,10 @@ before do login_as(form_manager) visit responses_admin_form_path(form) - click_on "Export" + click_on "Download" end - it 'includes form attributes' do - sleep 1.0 # TODO: remove, Remove all `sleep`s + it 'remains on page' do expect(page.current_path).to eq(responses_admin_form_path(form)) end end @@ -1784,13 +1783,13 @@ within('table.submissions') do expect(page).to have_content(submission.answer_01) end - expect_responses_fiscal_year_dropdown + expect_start_and_end_date_fields end end end context 'when Submissions exist for an a11_v2 form' do - describe 'click the combine export button' do + describe 'click the combined download button' do let(:form) { FactoryBot.create(:form, :a11_v2, organization:) } let!(:submission) { FactoryBot.create(:submission, form:, answer_01: "1") } @@ -1798,10 +1797,9 @@ visit responses_admin_form_path(form) end - it 'click the combine export button then see a flash message for an async job' do - within(".form-and-a11-fiscal-year") do - click_on("Export") - end + it 'click the combine download button then see a flash message for an async job' do + expect(page).to have_content("to see form and A-11 responses together") + click_on("Download A11-v2 + Form Responses") expect(page).to have_content("Touchpoints has initiated an asynchronous job that will take a few minutes.") end end diff --git a/spec/support/touchpoints_spec_helpers.rb b/spec/support/touchpoints_spec_helpers.rb index c250429c6..9308c0e35 100644 --- a/spec/support/touchpoints_spec_helpers.rb +++ b/spec/support/touchpoints_spec_helpers.rb @@ -1,6 +1,14 @@ # frozen_string_literal: true module TouchpointsSpecHelpers + def expect_start_and_end_date_fields + expect(page).to have_text('Export responses') + expect(page).to have_text('Start Date') + expect(page).to have_text('End Date') + expect(page).to have_css("input#start-date[type='date']") + expect(page).to have_css("input#end-date[type='date']") + end + def expect_responses_fiscal_year_dropdown expect(page).to have_text('Export responses') expect(page).to have_text('Select Fiscal Year:')