From a4a92e9ea6a19367675f17c53a4521e01aaa480f Mon Sep 17 00:00:00 2001 From: Pierre Date: Fri, 5 Jan 2024 14:00:27 +0100 Subject: [PATCH 1/8] feat: Responsive Filtering Implementation - Updated Meilisearch to consider event_id and year - Integrated Stimulus and form for responsive filtering with Tailwind - Added filter parameters in the Meilisearch controller --- app/controllers/talks_controller.rb | 17 ++- .../controllers/auto_submit_controller.js | 10 ++ .../controllers/filters_controller.js | 34 +++++ .../controllers/section_controller.js | 32 +++++ app/models/talk.rb | 9 +- app/views/talks/_filters_section.html.erb | 125 ++++++++++++++++++ app/views/talks/index.html.erb | 1 + tailwind.config.js | 4 +- 8 files changed, 227 insertions(+), 5 deletions(-) create mode 100644 app/javascript/controllers/filters_controller.js create mode 100644 app/javascript/controllers/section_controller.js create mode 100644 app/views/talks/_filters_section.html.erb diff --git a/app/controllers/talks_controller.rb b/app/controllers/talks_controller.rb index d74767b7..d238db14 100644 --- a/app/controllers/talks_controller.rb +++ b/app/controllers/talks_controller.rb @@ -6,8 +6,8 @@ class TalksController < ApplicationController # GET /talks def index session[:talks_page] = params[:page] || 1 - if params[:q].present? - talks = Talk.includes(:speakers, :event).pagy_search(params[:q]) + if params[:q].present? || params[:years].present? || params[:event_ids].present? + talks = Talk.includes(:speakers, :event).pagy_search(params[:q], filter: filtered_params) @pagy, @talks = pagy_meilisearch(talks, items: 9, page: session[:talks_page]&.to_i || 1) else @pagy, @talks = pagy(Talk.all.order(date: :desc).includes(:speakers, :event), items: 9, page: session[:talks_page]&.to_i || 1) @@ -37,6 +37,19 @@ def update private + # Generates filters for talks based on non-empty 'years' and 'event_ids' parameters. + # + # @return [Array] Array of filters. + def filtered_params + years = params[:years].reject(&:empty?) + event_ids = params[:event_ids].reject(&:empty?) + + filters = [] + filters += ["year IN #{years}"] unless years.empty? + filters += ["event_id IN #{event_ids}"] unless event_ids.empty? + filters + end + # Use callbacks to share common setup or constraints between actions. def set_talk @talk = Talk.includes(:speakers, :event).find_by(slug: params[:slug]) diff --git a/app/javascript/controllers/auto_submit_controller.js b/app/javascript/controllers/auto_submit_controller.js index e374820f..d44e70da 100644 --- a/app/javascript/controllers/auto_submit_controller.js +++ b/app/javascript/controllers/auto_submit_controller.js @@ -7,15 +7,25 @@ export default class extends Controller { initialize () { useDebounce(this) + this.element.querySelectorAll('input[type="checkbox"]').forEach((checkbox) => { + checkbox.addEventListener('change', () => this.submit()) + }) this.element.addEventListener('keydown', () => this.submit()) this.element.addEventListener('search', () => this.submit()) } disconnect () { + this.element.querySelectorAll('input[type="checkbox"]').forEach((checkbox) => { + checkbox.removeEventListener('change', () => this.submit()) + }) this.element.removeEventListener('keydown', () => this.submit()) this.element.removeEventListener('search', () => this.submit()) } + handleCheckbox (event) { + event.preventDefault() + } + submit () { this.element.requestSubmit() } diff --git a/app/javascript/controllers/filters_controller.js b/app/javascript/controllers/filters_controller.js new file mode 100644 index 00000000..d980f262 --- /dev/null +++ b/app/javascript/controllers/filters_controller.js @@ -0,0 +1,34 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + static targets = ['overlay', 'menu'] + + connect () { + this.overlayTarget.classList.remove('hidden') + this.menuTarget.classList.remove('hidden') + } + + closeVisibility () { + this.overlayTarget.classList.add('hidden') + this.menuTarget.classList.add('hidden') + } + + toggleVisibility () { + this.overlayTarget.classList.toggle('hidden') + this.menuTarget.classList.toggle('hidden') + } + + syncCheckboxes (event) { + const responsiveCheckbox = event.target + let responsiveCheckboxId = responsiveCheckbox.id + + // change the id of the checkbox to match the other one + if (responsiveCheckboxId.includes('mobile-')) { + responsiveCheckboxId = responsiveCheckboxId.replace('filter-mobile-', 'filter-') + } else { + responsiveCheckboxId = responsiveCheckboxId.replace('filter-', 'filter-mobile-') + } + + document.getElementById(responsiveCheckboxId).checked = responsiveCheckbox.checked + } +} diff --git a/app/javascript/controllers/section_controller.js b/app/javascript/controllers/section_controller.js new file mode 100644 index 00000000..16f96835 --- /dev/null +++ b/app/javascript/controllers/section_controller.js @@ -0,0 +1,32 @@ +import { Controller } from '@hotwired/stimulus' + +export default class extends Controller { + static targets = ['content', 'plus', 'minus'] + + connect () { + this.showMinusIcon() + } + + toggleVisibility () { + this.contentTarget.classList.toggle('hidden') + this.toggleIcons() + } + + toggleIcons () { + if (this.contentTarget.classList.contains('hidden')) { + this.showPlusIcon() + } else { + this.showMinusIcon() + } + } + + showPlusIcon () { + this.plusTarget.classList.remove('hidden') + this.minusTarget.classList.add('hidden') + } + + showMinusIcon () { + this.plusTarget.classList.add('hidden') + this.minusTarget.classList.remove('hidden') + } +} diff --git a/app/models/talk.rb b/app/models/talk.rb index 44bc0364..792c41f1 100644 --- a/app/models/talk.rb +++ b/app/models/talk.rb @@ -52,14 +52,19 @@ class Talk < ApplicationRecord attribute :thumbnail_sm attribute :thumbnail_md attribute :thumbnail_lg + attribute :year attribute :speaker_names do speakers.pluck(:name) end attribute :event_name do event_name end - searchable_attributes [:title, :description, :speaker_names, :event_name] - sortable_attributes [:title] + attribute :event_id do + event_id + end + filterable_attributes [:year, :event_id] + searchable_attributes [:title, :description, :speaker_names, :year, :event_name] + sortable_attributes [:title, :year] attributes_to_highlight ["*"] end diff --git a/app/views/talks/_filters_section.html.erb b/app/views/talks/_filters_section.html.erb new file mode 100644 index 00000000..c09fbc0e --- /dev/null +++ b/app/views/talks/_filters_section.html.erb @@ -0,0 +1,125 @@ +
+
+ + +
+
+
+
+

+ +

+
+
+ <%= form.collection_check_boxes(:years, Talk.pluck(:year).compact.uniq.sort, :to_s, :to_s) do |year| %> +
+ <%= year.check_box(data: { action: "change->filters#syncCheckboxes" }, id: "filter-mobile-year-#{year.value}", class: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500") %> + <%= label_tag "filter-mobile-year-#{year.value}", year.text, class: "ml-3 min-w-0 flex-1 text-gray-500" %> +
+ <% end %> +
+
+
+ +
+

+ +

+
+
+ <%= form.collection_check_boxes(:event_ids, confs, :first, :second) do |talk| %> +
+ <%= talk.check_box(class: "h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-500") %> + <%= label_tag talk.value, talk.text, class: "ml-3 min-w-0 flex-1 text-gray-500" %> +
+ <% end %> +
+
+
+
+
+
+
+
diff --git a/app/views/talks/index.html.erb b/app/views/talks/index.html.erb index 122b0691..e243965c 100644 --- a/app/views/talks/index.html.erb +++ b/app/views/talks/index.html.erb @@ -3,6 +3,7 @@ <%= form_with url: talks_path, method: :get, data: {controller: "auto-submit", turbo_frame: "talks", turbo_action: "advance"} do |form| %> <%= form.search_field :q, placeholder: "Search a talk (experimental)", class: "w-full" %> + <%= render partial: "filters_section", locals: { form: form } %> <% end %> <%= turbo_frame_tag "talks", target: "_top" do %> diff --git a/tailwind.config.js b/tailwind.config.js index 20d1e97a..e004ed7e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -73,6 +73,8 @@ module.exports = { ] }, plugins: [ - require('@tailwindcss/forms'), require('daisyui') + require('@tailwindcss/forms'), + require('daisyui'), + require('@tailwindcss/forms') ] } From 39751932efb2a85ffcfde0367725c84e43b48dc4 Mon Sep 17 00:00:00 2001 From: Pierre Date: Fri, 5 Jan 2024 14:23:36 +0100 Subject: [PATCH 2/8] [Linter check]: update --- app/views/talks/_filters_section.html.erb | 8 ++++---- app/views/talks/index.html.erb | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/app/views/talks/_filters_section.html.erb b/app/views/talks/_filters_section.html.erb index c09fbc0e..355e44b6 100644 --- a/app/views/talks/_filters_section.html.erb +++ b/app/views/talks/_filters_section.html.erb @@ -1,6 +1,6 @@
-