Add ajax search controller

This commit is contained in:
Ahmed Ejaz
2026-03-31 01:54:02 +05:00
parent 8ba0ab6b5a
commit 3ee338fa8d
8 changed files with 306 additions and 21 deletions

View File

@@ -1,23 +1,21 @@
# frozen_string_literal: true
module Products
module AjaxSearch
extend ActiveSupport::Concern
def search_producers
module Admin
class AjaxSearchController < Spree::Admin::BaseController
def 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
def categories
query = Spree::Taxon.all
render json: build_search_response(query)
end
def search_tax_categories
def tax_categories
query = Spree::TaxCategory.all
render json: build_search_response(query)

View File

@@ -4,7 +4,6 @@
module Admin
class ProductsV3Controller < Spree::Admin::BaseController
helper ProductsHelper
include ::Products::AjaxSearch
before_action :init_filters_params
before_action :init_pagination_params

View File

@@ -228,12 +228,11 @@ module Spree
:destroy,
:destroy_variant,
:clone,
:create_linked_variant,
:search_producers,
:search_categories,
:search_tax_categories
:create_linked_variant
], :products_v3
can [:admin, :producers, :categories, :tax_categories], :ajax_search
can [:create], Spree::Variant
can [:admin, :index, :read, :edit,
:update, :search, :delete, :destroy], Spree::Variant do |variant|

View File

@@ -13,7 +13,7 @@
aria_label: t('.producers.label'),
options: selected_option(producer_id, Enterprise),
selected_option: producer_id,
remote_url: admin_products_search_producers_url,
remote_url: admin_ajax_search_producers_url,
include_blank: t('.all_producers'),
placeholder_value: t('.search_for_producers')))
.categories
@@ -22,7 +22,7 @@
aria_label: t('.categories.label'),
options: selected_option(category_id, Spree::Taxon),
selected_option: category_id,
remote_url: admin_products_search_categories_url,
remote_url: admin_ajax_search_categories_url,
include_blank: t('.all_categories'),
placeholder_value: t('.search_for_categories')))
-if variant_tag_enabled?(spree_current_user)

View File

@@ -61,7 +61,7 @@
aria_label: t('.producer_field_name'),
options: variant.supplier_id ? [[variant.supplier.name, variant.supplier_id]] : [],
selected_option: variant.supplier_id,
remote_url: admin_products_search_producers_url,
remote_url: admin_ajax_search_producers_url,
placeholder_value: t('admin.products_v3.filters.select_producer')))
= error_message_on variant, :supplier
%td.col-category.field.naked_inputs
@@ -70,7 +70,7 @@
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'),
remote_url: admin_products_search_categories_url,
remote_url: admin_ajax_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
@@ -80,7 +80,7 @@
selected_option: variant.tax_category_id,
aria_label: t('.tax_category_field_name'),
include_blank: t('.none_tax_category'),
remote_url: admin_products_search_tax_categories_url,
remote_url: admin_ajax_search_tax_categories_url,
placeholder_value: t('.search_for_tax_categories')))
= error_message_on variant, :tax_category
- if variant_tag_enabled?(spree_current_user)

View File

@@ -27,7 +27,7 @@ FileUtils.chdir APP_ROOT do
system("bundle check 2> /dev/null") || system!(BUNDLE_ENV, "bundle install")
# Install JavaScript dependencies
system!("script/nodenv-install.sh")
system("script/nodenv-install.sh")
system!("bin/yarn")
# puts "\n== Copying sample files =="

View File

@@ -83,9 +83,13 @@ Openfoodnetwork::Application.routes.draw do
delete 'products_v3/destroy_variant/:id', to: 'products_v3#destroy_variant', as: 'destroy_variant'
post 'clone/:id', to: 'products_v3#clone', as: 'clone_product'
post 'products/create_linked_variant', to: 'products_v3#create_linked_variant', as: 'create_linked_variant'
get 'products_v3/search_producers', to: 'products_v3#search_producers', as: 'products_search_producers'
get 'products_v3/search_categories', to: 'products_v3#search_categories', as: 'products_search_categories'
get 'products_v3/search_tax_categories', to: 'products_v3#search_tax_categories', as: 'products_search_tax_categories'
scope :ajax_search, as: :ajax_search, controller: :ajax_search do
get :producers
get :categories
get :tax_categories
end
resources :product_preview, only: [:show]
resources :variant_overrides do

View File

@@ -0,0 +1,285 @@
# frozen_string_literal: true
RSpec.describe "Admin::AjaxSearch" do
include AuthenticationHelper
let(:admin_user) { create(:admin_user) }
let(:regular_user) { create(:user) }
describe "GET /admin/ajax_search/producers" do
context "when user is not logged in" do
it "redirects to login" do
get admin_ajax_search_producers_path
expect(response).to redirect_to %r|#/login$|
end
end
context "when user is logged in without permissions" do
before { login_as regular_user }
it "redirects to unauthorized" do
get admin_ajax_search_producers_path
expect(response).to redirect_to('/unauthorized')
end
end
context "when user is an admin" do
before { login_as admin_user }
let!(:producer1) { create(:supplier_enterprise, name: "Apple Farm") }
let!(:producer2) { create(:supplier_enterprise, name: "Berry Farm") }
let!(:producer3) { create(:supplier_enterprise, name: "Cherry Orchard") }
let!(:distributor) { create(:distributor_enterprise, name: "Distributor") }
it "returns producers sorted alphabetically by name" do
get admin_ajax_search_producers_path
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Apple Farm', 'Berry Farm',
'Cherry Orchard'])
expect(json_response["pagination"]["more"]).to be false
end
it "filters producers by search query" do
get admin_ajax_search_producers_path, params: { q: "berry" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Berry Farm'])
expect(json_response["results"].pluck("value")).to eq([producer2.id])
end
it "filters are case insensitive" do
get admin_ajax_search_producers_path, params: { q: "BERRY" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Berry Farm'])
end
it "filters with partial matches" do
get admin_ajax_search_producers_path, params: { q: "Farm" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Apple Farm', 'Berry Farm'])
end
it "excludes non-producer enterprises" do
get admin_ajax_search_producers_path
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).not_to include('Distributor')
end
context "with more than 30 producers" do
before do
create_list(:supplier_enterprise, 35) do |enterprise, i|
enterprise.update!(name: "Producer #{(i + 1).to_s.rjust(2, '0')}")
end
end
it "returns first page with 30 results and more flag as true" do
get admin_ajax_search_producers_path, params: { page: 1 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on second page with more flag as false" do
get admin_ajax_search_producers_path, params: { page: 2 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(8)
expect(json_response["pagination"]["more"]).to be false
end
end
end
context "when user has enterprise permissions" do
let!(:my_producer) { create(:supplier_enterprise, name: "My Producer") }
let!(:other_producer) { create(:supplier_enterprise, name: "Other Producer") }
let(:user_with_producer) { create(:user, enterprises: [my_producer]) }
before { login_as user_with_producer }
it "returns only managed producers" do
get admin_ajax_search_producers_path
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['My Producer'])
expect(json_response["results"].pluck("label")).not_to include('Other Producer')
end
end
end
describe "GET /admin/ajax_search/categories" do
context "when user is not logged in" do
it "redirects to login" do
get admin_ajax_search_categories_path
expect(response).to redirect_to %r|#/login$|
end
end
context "when user is logged in without permissions" do
before { login_as regular_user }
it "redirects to unauthorized" do
get admin_ajax_search_categories_path
expect(response).to redirect_to('/unauthorized')
end
end
context "when user is an admin" do
before { login_as admin_user }
let!(:category1) { create(:taxon, name: "Vegetables") }
let!(:category2) { create(:taxon, name: "Fruits") }
let!(:category3) { create(:taxon, name: "Dairy") }
it "returns categories sorted alphabetically by name" do
get admin_ajax_search_categories_path
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Dairy', 'Fruits', 'Vegetables'])
expect(json_response["pagination"]["more"]).to be false
end
it "filters categories by search query" do
get admin_ajax_search_categories_path, params: { q: "fruit" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Fruits'])
expect(json_response["results"].pluck("value")).to eq([category2.id])
end
it "filters are case insensitive" do
get admin_ajax_search_categories_path, params: { q: "VEGETABLES" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Vegetables'])
end
it "filters with partial matches" do
get admin_ajax_search_categories_path, params: { q: "ege" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['Vegetables'])
end
context "with more than 30 categories" do
before do
create_list(:taxon, 35) do |taxon, i|
taxon.update!(name: "Category #{(i + 1).to_s.rjust(2, '0')}")
end
end
it "returns first page with 30 results and more flag as true" do
get admin_ajax_search_categories_path, params: { page: 1 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on second page with more flag as false" do
get admin_ajax_search_categories_path, params: { page: 2 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(8)
expect(json_response["pagination"]["more"]).to be false
end
end
end
end
describe "GET /admin/ajax_search/tax_categories" do
context "when user is not logged in" do
it "redirects to login" do
get admin_ajax_search_tax_categories_path
expect(response).to redirect_to %r|#/login$|
end
end
context "when user is logged in without permissions" do
before { login_as regular_user }
it "redirects to unauthorized" do
get admin_ajax_search_tax_categories_path
expect(response).to redirect_to('/unauthorized')
end
end
context "when user is an admin" do
before { login_as admin_user }
let!(:tax_cat1) { create(:tax_category, name: "GST") }
let!(:tax_cat2) { create(:tax_category, name: "VAT") }
let!(:tax_cat3) { create(:tax_category, name: "No Tax") }
it "returns tax categories sorted alphabetically by name" do
get admin_ajax_search_tax_categories_path
expect(response).to have_http_status(:ok)
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['GST', 'No Tax', 'VAT'])
expect(json_response["pagination"]["more"]).to be false
end
it "filters tax categories by search query" do
get admin_ajax_search_tax_categories_path, params: { q: "vat" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['VAT'])
expect(json_response["results"].pluck("value")).to eq([tax_cat2.id])
end
it "filters are case insensitive" do
get admin_ajax_search_tax_categories_path, params: { q: "GST" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['GST'])
end
it "filters with partial matches" do
get admin_ajax_search_tax_categories_path, params: { q: "tax" }
json_response = response.parsed_body
expect(json_response["results"].pluck("label")).to eq(['No Tax'])
end
context "with more than 30 tax categories" do
before do
create_list(:tax_category, 35) do |tax_cat, i|
tax_cat.update!(name: "Tax Category #{(i + 1).to_s.rjust(2, '0')}")
end
end
it "returns first page with 30 results and more flag as true" do
get admin_ajax_search_tax_categories_path, params: { page: 1 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(30)
expect(json_response["pagination"]["more"]).to be true
end
it "returns remaining results on second page with more flag as false" do
get admin_ajax_search_tax_categories_path, params: { page: 2 }
json_response = response.parsed_body
expect(json_response["results"].length).to eq(8)
expect(json_response["pagination"]["more"]).to be false
end
end
end
end
end