mirror of
https://github.com/openfoodfoundation/openfoodnetwork
synced 2026-04-02 06:51:40 +00:00
Add searchable dropdowns for producers, categories, and tax categories in products_v3
This commit is contained in:
@@ -4,6 +4,7 @@
|
||||
module Admin
|
||||
class ProductsV3Controller < Spree::Admin::BaseController
|
||||
helper ProductsHelper
|
||||
include ::Products::AjaxSearch
|
||||
|
||||
before_action :init_filters_params
|
||||
before_action :init_pagination_params
|
||||
|
||||
58
app/controllers/concerns/products/ajax_search.rb
Normal file
58
app/controllers/concerns/products/ajax_search.rb
Normal file
@@ -0,0 +1,58 @@
|
||||
# frozen_string_literal: true
|
||||
|
||||
module Products
|
||||
module AjaxSearch
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
def search_producers
|
||||
query = OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
.managed_product_enterprises.is_primary_producer.by_name
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
def search_categories
|
||||
query = Spree::Taxon.all
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
def search_tax_categories
|
||||
query = Spree::TaxCategory.all
|
||||
|
||||
render json: build_search_response(query)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def build_search_response(query)
|
||||
page = (params[:page] || 1).to_i
|
||||
per_page = 30
|
||||
|
||||
filtered_query = apply_search_filter(query)
|
||||
total_count = filtered_query.size
|
||||
items = paginated_items(filtered_query, page, per_page)
|
||||
results = format_results(items)
|
||||
|
||||
{ results: results, pagination: { more: (page * per_page) < total_count } }
|
||||
end
|
||||
|
||||
def apply_search_filter(query)
|
||||
search_term = params[:q]
|
||||
return query if search_term.blank?
|
||||
|
||||
escaped_search_term = ActiveRecord::Base.sanitize_sql_like(search_term)
|
||||
pattern = "%#{escaped_search_term}%"
|
||||
|
||||
query.where('name ILIKE ?', pattern)
|
||||
end
|
||||
|
||||
def paginated_items(query, page, per_page)
|
||||
query.order(:name).offset((page - 1) * per_page).limit(per_page).pluck(:name, :id)
|
||||
end
|
||||
|
||||
def format_results(items)
|
||||
items.map { |label, value| { value:, label: } }
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -52,5 +52,15 @@ module Admin
|
||||
@allowed_source_producers ||= OpenFoodNetwork::Permissions.new(spree_current_user)
|
||||
.enterprises_granting_linked_variants
|
||||
end
|
||||
|
||||
# Query only name of the model to avoid loading the whole record
|
||||
def selected_option(id, model)
|
||||
return [] unless id
|
||||
|
||||
name = model.where(id: id).pick(:name)
|
||||
return [] unless name
|
||||
|
||||
[[name, id]]
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -221,9 +221,18 @@ module Spree
|
||||
OpenFoodNetwork::Permissions.new(user).
|
||||
enterprises_granting_linked_variants.include? variant.supplier
|
||||
end
|
||||
|
||||
can [:admin, :index, :bulk_update, :destroy, :destroy_variant, :clone,
|
||||
:create_linked_variant], :products_v3
|
||||
can [
|
||||
:admin,
|
||||
:index,
|
||||
:bulk_update,
|
||||
:destroy,
|
||||
:destroy_variant,
|
||||
:clone,
|
||||
:create_linked_variant,
|
||||
:search_producers,
|
||||
:search_categories,
|
||||
:search_tax_categories
|
||||
], :products_v3
|
||||
|
||||
can [:create], Spree::Variant
|
||||
can [:admin, :index, :read, :edit,
|
||||
|
||||
@@ -9,14 +9,22 @@
|
||||
- if producer_options.many?
|
||||
.producers
|
||||
= label_tag :producer_id, t('.producers.label')
|
||||
= select_tag :producer_id, options_for_select(producer_options, producer_id),
|
||||
include_blank: t('.all_producers'), class: "fullwidth",
|
||||
data: { "controller": "tom-select", 'tom-select-placeholder-value': t('.search_for_producers')}
|
||||
= render(SearchableDropdownComponent.new(name: :producer_id,
|
||||
aria_label: t('.producer_field_name'),
|
||||
options: selected_option(producer_id, Enterprise),
|
||||
selected_option: producer_id,
|
||||
remote_url: admin_products_search_producers_url,
|
||||
include_blank: t('.all_producers'),
|
||||
placeholder_value: t('.search_for_producers')))
|
||||
.categories
|
||||
= label_tag :category_id, t('.categories.label')
|
||||
= select_tag :category_id, options_for_select(category_options, category_id),
|
||||
include_blank: t('.all_categories'), class: "fullwidth",
|
||||
data: { "controller": "tom-select", 'tom-select-placeholder-value': t('.search_for_categories')}
|
||||
= render(SearchableDropdownComponent.new(name: :category_id,
|
||||
aria_label: t('.category_field_name'),
|
||||
options: selected_option(category_id, Spree::Taxon),
|
||||
selected_option: category_id,
|
||||
remote_url: admin_products_search_categories_url,
|
||||
include_blank: t('.all_categories'),
|
||||
placeholder_value: t('.search_for_categories')))
|
||||
-if variant_tag_enabled?(spree_current_user)
|
||||
.tags
|
||||
= label_tag :tags_name_in, t('.tags.label')
|
||||
|
||||
@@ -59,27 +59,27 @@
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :supplier_id,
|
||||
aria_label: t('.producer_field_name'),
|
||||
options: producer_options,
|
||||
options: variant.supplier_id ? [[variant.supplier.name, variant.supplier_id]] : [],
|
||||
selected_option: variant.supplier_id,
|
||||
include_blank: t('admin.products_v3.filters.select_producer'),
|
||||
remote_url: admin_products_search_producers_url,
|
||||
placeholder_value: t('admin.products_v3.filters.select_producer')))
|
||||
= error_message_on variant, :supplier
|
||||
%td.col-category.field.naked_inputs
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :primary_taxon_id,
|
||||
options: category_options,
|
||||
options: variant.primary_taxon_id ? [[variant.primary_taxon.name, variant.primary_taxon_id]] : [],
|
||||
selected_option: variant.primary_taxon_id,
|
||||
aria_label: t('.category_field_name'),
|
||||
include_blank: t('admin.products_v3.filters.select_category'),
|
||||
remote_url: admin_products_search_categories_url,
|
||||
placeholder_value: t('admin.products_v3.filters.select_category')))
|
||||
= error_message_on variant, :primary_taxon
|
||||
%td.col-tax_category.field.naked_inputs
|
||||
= render(SearchableDropdownComponent.new(form: f,
|
||||
name: :tax_category_id,
|
||||
options: tax_category_options,
|
||||
options: variant.tax_category_id ? [[variant.tax_category.name, variant.tax_category_id]] : [],
|
||||
selected_option: variant.tax_category_id,
|
||||
include_blank: t('.none_tax_category'),
|
||||
aria_label: t('.tax_category_field_name'),
|
||||
remote_url: admin_products_search_tax_categories_url,
|
||||
placeholder_value: t('.search_for_tax_categories')))
|
||||
= error_message_on variant, :tax_category
|
||||
- if variant_tag_enabled?(spree_current_user)
|
||||
|
||||
@@ -83,6 +83,9 @@ export default class extends Controller {
|
||||
}
|
||||
|
||||
#addRemoteOptions(options) {
|
||||
// by default, for dropdown_input plugin, it's true. Otherwise for multi-select it's false
|
||||
// it should always be true so to invoke the onDropdownOpen to fetch options
|
||||
options.shouldOpen = true;
|
||||
this.openedByClick = false;
|
||||
|
||||
options.firstUrl = (query) => {
|
||||
@@ -91,12 +94,9 @@ export default class extends Controller {
|
||||
|
||||
options.load = this.#fetchOptions.bind(this);
|
||||
|
||||
options.onFocus = function () {
|
||||
this.control.load("", () => {});
|
||||
}.bind(this);
|
||||
|
||||
options.onDropdownOpen = function () {
|
||||
this.openedByClick = true;
|
||||
this.control.load("", () => {});
|
||||
}.bind(this);
|
||||
|
||||
options.onType = function () {
|
||||
|
||||
Reference in New Issue
Block a user