diff --git a/app/controllers/collections_controller.rb b/app/controllers/collections_controller.rb index 0877f89f6cf..ff7cd8b5c55 100644 --- a/app/controllers/collections_controller.rb +++ b/app/controllers/collections_controller.rb @@ -60,19 +60,24 @@ def index def list_challenges @page_subtitle = "Open Challenges" @hide_dashboard = true - - @challenge_collections = (CollectionSearchForm.new(challenge_type: "GiftExchange", signup_open: true, sort_column: "signups_close_at", page: 1, per_page: 15).search_results.to_a + - CollectionSearchForm.new(challenge_type: "PromptMeme", signup_open: true, sort_column: "signups_close_at", page: 1, per_page: 15).search_results.to_a) + @sort_and_filter = true + @show_type_filter = true + @search = CollectionSearchForm.new(challenge_filter_params.merge(signup_open: true, sort_column: "signups_close_at", page: params[:page])) + @challenge_collections = @search.search_results end def list_ge_challenges @page_subtitle = "Open Gift Exchange Challenges" - @challenge_collections = CollectionSearchForm.new(challenge_type: "GiftExchange", signup_open: true, sort_column: "signups_close_at", page: 1, per_page: 15).search_results + @sort_and_filter = true + @search = CollectionSearchForm.new(challenge_filter_params.merge(challenge_type: "GiftExchange", signup_open: true, sort_column: "signups_close_at", page: params[:page])) + @challenge_collections = @search.search_results end def list_pm_challenges @page_subtitle = "Open Prompt Meme Challenges" - @challenge_collections = CollectionSearchForm.new(challenge_type: "PromptMeme", signup_open: true, sort_column: "signups_close_at", page: 1, per_page: 15).search_results + @sort_and_filter = true + @search = CollectionSearchForm.new(challenge_filter_params.merge(challenge_type: "PromptMeme", signup_open: true, sort_column: "signups_close_at", page: params[:page])) + @challenge_collections = @search.search_results end def show @@ -215,4 +220,10 @@ def collection_params ] ) end + + def challenge_filter_params + params.permit(:commit, collection_search: [ + :title, :tag, :challenge_type, :moderated, :multifandom, :closed + ])[:collection_search] || {} + end end diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index a0f2435679a..5a98197d1f6 100755 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -57,8 +57,12 @@ def classes_for_main class_names end + COLLECTION_FILTER_ACTIONS = %w[index list_challenges list_ge_challenges list_pm_challenges].freeze + def page_has_filters? - @facets.present? || (controller.action_name == 'index' && controller.controller_name == 'collections') || (controller.action_name == 'unassigned' && controller.controller_name == 'fandoms') + @facets.present? || # rubocop:disable Rails/HelperInstanceVariable + (controller.controller_name == "collections" && COLLECTION_FILTER_ACTIONS.include?(controller.action_name)) || + (controller.controller_name == "fandoms" && controller.action_name == "unassigned") end # This is used to make the current page we're on (determined by the path or by the specified condition) a span with class "current" and it allows us to add a title attribute to the link or the span diff --git a/app/models/search/collection_query.rb b/app/models/search/collection_query.rb index 61792360dad..426b47d8ad4 100644 --- a/app/models/search/collection_query.rb +++ b/app/models/search/collection_query.rb @@ -57,7 +57,12 @@ def signup_open_filter end def signup_closes_in_future_filter - { range: { signups_close_at: { gt: "now" } } } if options[:signup_open].present? + return if options[:signup_open].blank? + + { bool: { should: [ + { range: { signups_close_at: { gt: "now" } } }, + { bool: { must_not: { exists: { field: "signups_close_at" } } } } + ] } } end def closed_filter diff --git a/app/views/collections/_challenge_collections.html.erb b/app/views/collections/_challenge_collections.html.erb index 830fd98a5c2..787afe70067 100644 --- a/app/views/collections/_challenge_collections.html.erb +++ b/app/views/collections/_challenge_collections.html.erb @@ -1,12 +1,29 @@ -<% if @challenge_collections.blank? %> -

<%= ts("There are no challenges currently open for sign-ups in the archive.") %>

-<% else %> -

<%= ts("The following challenges are currently open for sign-ups! Those closing soonest are at the top.") %>

+

+ <% if @challenge_collections.blank? %> + <%= t("collections.challenge_collections.no_challenges") %> + <% else %> + <%= search_header @challenge_collections, @query, t("collections.challenge_collections.search_header_label") %> + <% end %> +

+ +<% unless @challenge_collections.blank? %> +

<%= t("collections.challenge_collections.open_description") %>

+ + <%= will_paginate @challenge_collections %> -

<%= ts("List of Collections") %>

+

<%= t("collections.challenge_collections.list_heading") %>

-<% end %> \ No newline at end of file + +<% end %> + +<% if @sort_and_filter %> + <%= render "challenge_filters" %> +<% end %> + +<% unless @challenge_collections.blank? %> + <%= will_paginate @challenge_collections %> +<% end %> diff --git a/app/views/collections/_challenge_filters.html.erb b/app/views/collections/_challenge_filters.html.erb new file mode 100644 index 00000000000..6ddec850a5c --- /dev/null +++ b/app/views/collections/_challenge_filters.html.erb @@ -0,0 +1,132 @@ +<%= form_for @search, as: :collection_search, url: request.path, html: { method: :get, class: "narrow-hidden filters", id: "collection-filters" } do |f| %> +

<%= t("collections.filters.landmark") %>

+ <%= field_set_tag t("collections.filters.legend") do %> +
+
<%= t("collections.filters.submit.landmark") %>
+
<%= submit_tag t("collections.filters.submit.button") %>
+ + + + + + + +
<%= t("collections.filters.multifandom.label") %>
+
+ +
+ +
<%= t("collections.filters.closed.label") %>
+
+ +
+ +
<%= t("collections.filters.moderated.label") %>
+
+ +
+ + <% if @show_type_filter %> +
<%= t("collections.filters.type.label") %>
+
+ +
+ <% end %> + +
<%= t("collections.filters.submit.landmark") %>
+
<%= submit_tag t("collections.filters.submit.button") %>
+
+

+ <%= link_to t("collections.filters.clear_filters"), request.path %> +

+ <% end %> + +<% end %> diff --git a/config/locales/views/en.yml b/config/locales/views/en.yml index c2e3a15cd7a..14136c7150f 100644 --- a/config/locales/views/en.yml +++ b/config/locales/views/en.yml @@ -753,6 +753,11 @@ en: meta: collection_tags: 'Collection tags:' collections: + challenge_collections: + list_heading: List of Challenges + no_challenges: There are no challenges currently open for sign-ups in the archive. + open_description: The following challenges are currently open for sign-ups! Those closing soonest are at the top. + search_header_label: Challenge collection_blurb: collection_tags: 'Collection Tags:' filters: diff --git a/features/collections/open_challenges.feature b/features/collections/open_challenges.feature new file mode 100644 index 00000000000..b60ea5cc5a1 --- /dev/null +++ b/features/collections/open_challenges.feature @@ -0,0 +1,157 @@ +@collections +Feature: Open Challenges + In order to find challenges currently accepting sign-ups + As a user + I want to browse and filter open challenges + + # list_challenges + + # merged GE + PM list + + Scenario: Open challenges page shows both gift exchanges and prompt memes + + Given a set of open challenges for searching + When I view open challenges + Then I should see "Open GE" + And I should see "Open PM" + + Scenario: Open challenges page includes challenges with no closing date + + Given a set of open challenges for searching + When I view open challenges + Then I should see "No Close PM" + + # filter form + + Scenario: Filter form appears on open challenges page + + Given a set of open challenges for searching + When I view open challenges + Then I should see "Sort and Filter" + + Scenario: Challenge type filter is shown on open challenges page + + Given a set of open challenges for searching + When I view open challenges + Then I should see "Collection Type" + + Scenario: No Challenge option is not available on open challenges filter + + Given a set of open challenges for searching + When I view open challenges + Then I should not see "No Challenge" + + Scenario: Sort options are not shown on open challenges filter + + Given a set of open challenges for searching + When I view open challenges + Then I should not see "Sort by" + + # filtering + + Scenario: Filter open challenges by gift exchange type + + Given a set of open challenges for searching + When I view open challenges + And I choose "Gift Exchange Challenge" + And I press "Sort and Filter" + Then I should see "Open GE" + And I should see "Moderated GE" + And I should not see "Open PM" + And I should not see "No Close PM" + + Scenario: Filter open challenges by prompt meme type + + Given a set of open challenges for searching + When I view open challenges + And I choose "Prompt Meme Challenge" + And I press "Sort and Filter" + Then I should see "Open PM" + And I should see "No Close PM" + And I should not see "Open GE" + + Scenario: Filter open challenges by title + + Given a set of open challenges for searching + When I view open challenges + And I fill in "collection_search_title" with "Open GE" + And I press "Sort and Filter" + Then I should see "Open GE" + And I should not see "Open PM" + And I should not see "No Close PM" + + Scenario: Filter open challenges by tag + + Given a set of open challenges for searching + When I view open challenges + And I fill in "collection_search_tag" with "Some cool tag" + And I press "Sort and Filter" + Then I should see "Open GE" + And I should not see "Open PM" + And I should not see "No Close PM" + + Scenario: Filter open challenges by moderated + + Given a set of open challenges for searching + When I view open challenges + And I choose "collection_search_moderated_true" + And I press "Sort and Filter" + Then I should see "Moderated GE" + And I should not see "Open GE" + And I should not see "Open PM" + + Scenario: Filter open challenges by closed collection + + Given a set of open challenges for searching + When I view open challenges + And I choose "collection_search_closed_true" + And I press "Sort and Filter" + Then I should see "Closed Collection GE" + And I should not see "Open GE" + And I should not see "Open PM" + + Scenario: Filter open challenges by multifandom + + Given a set of open challenges for searching + When I view open challenges + And I choose "collection_search_multifandom_true" + And I press "Sort and Filter" + Then I should see "Multifandom GE" + And I should not see "Open GE" + And I should not see "Open PM" + + # list_ge_challenges + + Scenario: Open gift exchange challenges page shows only gift exchanges + + Given a set of open challenges for searching + When I view open challenges + And I follow "Gift Exchange Challenges" + Then I should see "Open GE" + And I should not see "Open PM" + And I should not see "No Close PM" + + Scenario: Challenge type filter is not shown on gift exchange challenges page + + Given a set of open challenges for searching + When I view open challenges + And I follow "Gift Exchange Challenges" + Then I should not see "Collection Type" + + # list_pm_challenges + + Scenario: Open prompt meme challenges page shows only prompt memes + + Given a set of open challenges for searching + When I view open challenges + And I follow "Prompt Meme Challenges" + Then I should see "Open PM" + And I should see "No Close PM" + And I should not see "Open GE" + + Scenario: Challenge type filter is not shown on prompt meme challenges page + + Given a set of open challenges for searching + When I view open challenges + And I follow "Prompt Meme Challenges" + Then I should not see "Collection Type" diff --git a/features/step_definitions/collection_steps.rb b/features/step_definitions/collection_steps.rb index 3b1307e5747..f587125657a 100644 --- a/features/step_definitions/collection_steps.rb +++ b/features/step_definitions/collection_steps.rb @@ -342,3 +342,56 @@ expect(page).to have_select("#{pseud.user.login}_role", selected: role) end end + +Given "a set of open challenges for searching" do + FactoryBot.create(:collection, + name: "open_ge", + title: "Open GE", + tag_string: "Some cool tag", + challenge: FactoryBot.create(:gift_exchange, + signup_open: true, + signups_open_at: Time.zone.now - 2.days, + signups_close_at: Time.zone.now + 1.week), + challenge_type: "GiftExchange") + FactoryBot.create(:collection, + name: "multifandom_ge", + title: "Multifandom GE", + multifandom: true, + challenge: FactoryBot.create(:gift_exchange, + signup_open: true, + signups_open_at: Time.zone.now - 1.day, + signups_close_at: Time.zone.now + 1.week), + challenge_type: "GiftExchange") + FactoryBot.create(:collection, + name: "open_pm", + title: "Open PM", + challenge: FactoryBot.create(:prompt_meme, + signup_open: true, + signups_open_at: Time.zone.now - 2.days, + signups_close_at: Time.zone.now + 1.week), + challenge_type: "PromptMeme") + FactoryBot.create(:collection, + name: "no_close_pm", + title: "No Close PM", + challenge: FactoryBot.create(:prompt_meme, signup_open: true, signups_open_at: Time.zone.now - 1.day), + challenge_type: "PromptMeme") + FactoryBot.create(:collection, + :moderated, + name: "moderated_ge", + title: "Moderated GE", + challenge: FactoryBot.create(:gift_exchange, + signup_open: true, + signups_open_at: Time.zone.now - 1.day, + signups_close_at: Time.zone.now + 1.week), + challenge_type: "GiftExchange") + FactoryBot.create(:collection, + :closed, + name: "closed_ge", + title: "Closed Collection GE", + challenge: FactoryBot.create(:gift_exchange, + signup_open: true, + signups_open_at: Time.zone.now - 1.day, + signups_close_at: Time.zone.now + 1.week), + challenge_type: "GiftExchange") + step %{all indexing jobs have been run} +end diff --git a/spec/controllers/collections_controller_spec.rb b/spec/controllers/collections_controller_spec.rb index fe38dc191eb..ec4d049d35a 100644 --- a/spec/controllers/collections_controller_spec.rb +++ b/spec/controllers/collections_controller_spec.rb @@ -259,6 +259,21 @@ expect(assigns(:challenge_collections)).to include prompt_meme_collection expect(assigns(:challenge_collections)).to include gift_exchange_collection end + + context "pagination" do + before { allow(ArchiveConfig).to receive(:ITEMS_PER_PAGE).and_return(1) } + + let!(:second_pm) { create(:prompt_meme, signup_open: true, signups_open_at: Time.zone.now - 1.day, signups_close_at: Time.zone.now + 2.weeks) } + let!(:second_pm_collection) { create(:collection, challenge: second_pm, challenge_type: "PromptMeme") } + + before { run_all_indexing_jobs } + + it "paginates results" do + get :list_challenges + expect(assigns(:challenge_collections).total_entries).to be > 1 + expect(assigns(:challenge_collections).size).to eq(1) + end + end end context "displays all open gift exchange challenges on list_ge_challenges index" do diff --git a/spec/models/search/collection_query_spec.rb b/spec/models/search/collection_query_spec.rb index 0220422822b..bea2f2e82e9 100644 --- a/spec/models/search/collection_query_spec.rb +++ b/spec/models/search/collection_query_spec.rb @@ -82,6 +82,15 @@ expect(query.search_results).not_to include signup_past_open_collection end + it "includes collections with no closing date when signup_open is true" do + no_close_pm = create(:prompt_meme, signup_open: true, signups_open_at: Time.current - 1.day) + no_close_collection = create(:collection, challenge: no_close_pm, challenge_type: "PromptMeme") + run_all_indexing_jobs + + query = CollectionQuery.new(signup_open: true) + expect(query.search_results).to include no_close_collection + end + it "filters collections by multifandom filter" do query = CollectionQuery.new(multifandom: true) expect(query.search_results).not_to include prompt_meme_collection