effective_search 0.2.3 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/app/assets/javascripts/effective_search/admin.js +62 -0
- data/app/assets/javascripts/effective_search/base.js +6 -10
- data/app/assets/stylesheets/effective_search/admin.scss +14 -0
- data/app/assets/stylesheets/effective_search.scss +1 -0
- data/app/controllers/admin/search_controller.rb +36 -0
- data/app/helpers/effective_search_helper.rb +6 -0
- data/app/models/concerns/effective_search_admin_search.rb +88 -0
- data/app/models/effective/admin_search.rb +5 -0
- data/app/views/admin/search/_applicant.html.haml +12 -0
- data/app/views/admin/search/_form_nav_icon.html.haml +3 -0
- data/app/views/admin/search/_layout.html.haml +2 -0
- data/app/views/admin/search/_modal.html.haml +8 -0
- data/app/views/admin/search/_order.html.haml +16 -0
- data/app/views/admin/search/_organization.html.haml +28 -0
- data/app/views/admin/search/_search.html.haml +41 -0
- data/app/views/admin/search/_user.html.haml +24 -0
- data/app/views/admin/search/index.html.haml +1 -0
- data/config/routes.rb +1 -0
- data/lib/effective_search/version.rb +1 -1
- data/lib/effective_search.rb +5 -1
- metadata +16 -2
checksums.yaml
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
---
|
|
2
2
|
SHA256:
|
|
3
|
-
metadata.gz:
|
|
4
|
-
data.tar.gz:
|
|
3
|
+
metadata.gz: a5bc0454808d5c92f57c4d8b5df6aec31c7c3a9d0b278c1824722c1a7598ffa2
|
|
4
|
+
data.tar.gz: fd62ab6ab6b636122745dda9053521f18f7c8fe6462cf2485b39ca5998a9b3ad
|
|
5
5
|
SHA512:
|
|
6
|
-
metadata.gz:
|
|
7
|
-
data.tar.gz:
|
|
6
|
+
metadata.gz: 5e359cf8e8ce4d0bd34941b3b7a7bb3530af955ef6fe461cec81e21e29000ef6f6781ecc4265c506984851a6e52856e50381531b9a95a973e834fc28a02b31a2
|
|
7
|
+
data.tar.gz: cfc31d57f4d4ab5200298730ee1da92def9cd672220d6156ea4ac72c72840b14e0f8f233082092329c70a74d99bb6cadc2fa9546348d502dc2de5932c1c3fddd
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
// Admin search handle pagination clicks
|
|
2
|
+
$(document).on('click', '#effective-search-admin-modal .page-link', function(event) {
|
|
3
|
+
event.preventDefault();
|
|
4
|
+
|
|
5
|
+
var $link = $(this);
|
|
6
|
+
var href = $link.attr('href');
|
|
7
|
+
|
|
8
|
+
// Extract the page parameter from the URL
|
|
9
|
+
var pageMatch = href.match(/[?&]page=(\d+)/);
|
|
10
|
+
if (!pageMatch) return;
|
|
11
|
+
|
|
12
|
+
var pageNumber = pageMatch[1];
|
|
13
|
+
|
|
14
|
+
// Find the closest form and update the hidden page field
|
|
15
|
+
var $form = $link.closest('form');
|
|
16
|
+
if ($form.length === 0) return;
|
|
17
|
+
|
|
18
|
+
var $pageField = $form.find('input[name="page"]');
|
|
19
|
+
if ($pageField.length === 0) return;
|
|
20
|
+
|
|
21
|
+
// Update the page value and submit the form
|
|
22
|
+
$pageField.val(pageNumber);
|
|
23
|
+
|
|
24
|
+
$form.find("[data-load-ajax-url]").trigger('reload');
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
// Admin search close the modal when they visit a search result page
|
|
28
|
+
$(document).on('click', '#effective-search-admin-modal a', function(event) {
|
|
29
|
+
var $link = $(event.currentTarget);
|
|
30
|
+
if($link.hasClass('page-link') || $link.data('effective-search-clear')) return;
|
|
31
|
+
|
|
32
|
+
$('#effective-search-admin-modal').modal('hide');
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Admin search clear button
|
|
36
|
+
$(document).on('click', '#effective-search-admin-modal a[data-effective-search-clear]', function(event) {
|
|
37
|
+
event.preventDefault();
|
|
38
|
+
|
|
39
|
+
var $form = $(event.currentTarget).closest('form')
|
|
40
|
+
|
|
41
|
+
$form.trigger('reset');
|
|
42
|
+
$form.find('#effective-search-results').html('');
|
|
43
|
+
$form.find('input[name="page"]').val(1);
|
|
44
|
+
$form.find('input[name="q[term]"]').focus();
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
// Admin search reset to page 1 when search term changes
|
|
48
|
+
$(document).on('keyup paste', "#effective-search-admin-modal [data-load-ajax-url]", function(event) {
|
|
49
|
+
var $form = $(event.currentTarget).closest('form')
|
|
50
|
+
$form.find('input[name="page"]').val(1);
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
// Prevent form submission on Enter key press
|
|
54
|
+
$(document).on('keydown keyup', "#effective-search-admin-modal form", function(event) {
|
|
55
|
+
if(event.key === 'Enter') { event.preventDefault(); }
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
// Auto focus the search input when the modal is shown
|
|
59
|
+
$(document).on('shown.bs.modal', '#effective-search-admin-modal', function () {
|
|
60
|
+
$(".effective-search-admin").find('input[type="search"]').first().focus();
|
|
61
|
+
});
|
|
62
|
+
|
|
@@ -1,11 +1,7 @@
|
|
|
1
|
-
$(document).on('
|
|
1
|
+
$(document).on('shown.bs.collapse', '#effective-search-icon-form', function () {
|
|
2
|
+
$("#effective-search-input").focus();
|
|
3
|
+
});
|
|
2
4
|
|
|
3
|
-
function
|
|
4
|
-
$(
|
|
5
|
-
|
|
6
|
-
})
|
|
7
|
-
|
|
8
|
-
$('#effective-search-icon-form').on('hidden.bs.collapse', function () {
|
|
9
|
-
$("#effective-search-input").blur()
|
|
10
|
-
})
|
|
11
|
-
}
|
|
5
|
+
$(document).on('hidden.bs.collapse', '#effective-search-icon-form', function () {
|
|
6
|
+
$("#effective-search-input").blur();
|
|
7
|
+
});
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
module Admin
|
|
2
|
+
class SearchController < ApplicationController
|
|
3
|
+
before_action(:authenticate_user!) if defined?(Devise)
|
|
4
|
+
before_action { EffectiveResources.authorize!(self, :admin, :effective_search) }
|
|
5
|
+
|
|
6
|
+
include Effective::CrudController
|
|
7
|
+
|
|
8
|
+
def index
|
|
9
|
+
EffectiveResources.authorize!(self, :index, EffectiveSearch.AdminSearch)
|
|
10
|
+
|
|
11
|
+
# But more often we do a full membership directory search screen
|
|
12
|
+
@search = build_search
|
|
13
|
+
@search.search!
|
|
14
|
+
|
|
15
|
+
@page_title = "Search"
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def build_search
|
|
19
|
+
search = EffectiveSearch.AdminSearch.new()
|
|
20
|
+
search.assign_attributes(search_params.select { |k, _| search.respond_to?("#{k}=") })
|
|
21
|
+
search.assign_attributes(current_user: current_user, view_context: view_context)
|
|
22
|
+
search
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def search_params
|
|
26
|
+
return {} unless params[:q].present?
|
|
27
|
+
|
|
28
|
+
if params[:q].respond_to?(:to_h) # From the search form
|
|
29
|
+
params.require(:q).permit!
|
|
30
|
+
else
|
|
31
|
+
{ term: params.permit(:q).fetch(:q) } # From the url /directory?q=asdf
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
end
|
|
36
|
+
end
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
# EffectiveSearchAdminSearch
|
|
4
|
+
# Mark your search model with include EffectiveSearchAdminSearch
|
|
5
|
+
|
|
6
|
+
module EffectiveSearchAdminSearch
|
|
7
|
+
extend ActiveSupport::Concern
|
|
8
|
+
|
|
9
|
+
module ClassMethods
|
|
10
|
+
def effective_search_admin_search?; true; end
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
included do
|
|
14
|
+
include ActiveModel::Model
|
|
15
|
+
|
|
16
|
+
attr_accessor :current_user
|
|
17
|
+
attr_accessor :view_context
|
|
18
|
+
|
|
19
|
+
attr_accessor :term
|
|
20
|
+
validates :term, length: { minimum: 1, allow_blank: true }
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def to_s
|
|
24
|
+
term.to_s
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def per_page
|
|
28
|
+
3
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
def present?
|
|
32
|
+
term.present?
|
|
33
|
+
end
|
|
34
|
+
|
|
35
|
+
def users
|
|
36
|
+
@users ||= Effective::Resource.new(current_user.class).search_any(term, columns: [:first_name, :last_name, :email])
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
def organizations
|
|
40
|
+
@organizations ||= Effective::Resource.new(EffectiveMemberships.Organization).search_any(term, columns: [:title, :email])
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def orders
|
|
44
|
+
@orders ||= Effective::Resource.new(Effective::Order).search_any(term, columns: [:id])
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def applicants
|
|
48
|
+
@applicants = EffectiveMemberships.Applicant.where(user: users).order(created_at: :desc)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Search and assigns the collection
|
|
52
|
+
# Assigns the entire collection() if there are no search terms
|
|
53
|
+
# Otherwise validate the search terms
|
|
54
|
+
def search!
|
|
55
|
+
@users = users()
|
|
56
|
+
@orders = orders()
|
|
57
|
+
@applicants = applicants()
|
|
58
|
+
@organizations = organizations()
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# The paginated results
|
|
62
|
+
def user_results(page: nil)
|
|
63
|
+
results(users, page: page)
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
def order_results(page: nil)
|
|
67
|
+
results(orders, page: page)
|
|
68
|
+
end
|
|
69
|
+
|
|
70
|
+
def applicant_results(page: nil)
|
|
71
|
+
results(applicants, page: page)
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def organization_results(page: nil)
|
|
75
|
+
results(organizations, page: page)
|
|
76
|
+
end
|
|
77
|
+
|
|
78
|
+
private
|
|
79
|
+
|
|
80
|
+
def results(collection, page: nil)
|
|
81
|
+
page = (page || 1).to_i
|
|
82
|
+
offset = [(page - 1), 0].max * per_page
|
|
83
|
+
|
|
84
|
+
collection.limit(per_page).offset(offset)
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
end
|
|
88
|
+
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
%tr
|
|
2
|
+
%td
|
|
3
|
+
%a{href: effective_memberships.edit_admin_applicant_path(applicant), title: applicant}
|
|
4
|
+
%span.badge.rounded-circle.p-2.mr-3.float-left= icon('clipboard')
|
|
5
|
+
= applicant.owner
|
|
6
|
+
%br
|
|
7
|
+
%small.text-muted
|
|
8
|
+
= applicant.summary_title
|
|
9
|
+
= ' • '.html_safe
|
|
10
|
+
= applicant.owner&.email
|
|
11
|
+
= ' • '.html_safe
|
|
12
|
+
= badge(applicant.status)
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
%div#effective-search-admin-modal.modal.fade{tabindex: '-1', role: 'dialog', 'data-turbolinks-permanent': true}
|
|
2
|
+
.modal-dialog.modal-lg{role: 'document'}
|
|
3
|
+
.modal-content
|
|
4
|
+
.modal-body
|
|
5
|
+
- search = EffectiveSearch.AdminSearch.new
|
|
6
|
+
= render('admin/search/search', search: search, modal: true)
|
|
7
|
+
.modal-footer.bg-light.p-1
|
|
8
|
+
%small.text-muted Search by name, email, #{etd(EffectiveMemberships.Organization)}, or order number.
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
%tr
|
|
2
|
+
%td
|
|
3
|
+
- path = (EffectiveResources.authorized?(self, :edit, order) ? effective_orders.admin_edit_order_path(order) : effective_orders.admin_order_path(order))
|
|
4
|
+
|
|
5
|
+
%a{href: path, title: order}
|
|
6
|
+
%span.badge.rounded-circle.p-2.mr-3.float-left= icon('shopping-cart')
|
|
7
|
+
= order.to_s
|
|
8
|
+
%br
|
|
9
|
+
%small.text-muted
|
|
10
|
+
= order.billing_name
|
|
11
|
+
= ' • '.html_safe
|
|
12
|
+
= order.email
|
|
13
|
+
= ' • '.html_safe
|
|
14
|
+
= price_to_currency(order.total)
|
|
15
|
+
= ' • '.html_safe
|
|
16
|
+
= badge(order.status)
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
%tr
|
|
2
|
+
%td
|
|
3
|
+
%a{href: effective_memberships.edit_admin_organization_path(organization), title: organization}
|
|
4
|
+
%span.badge.rounded-circle.p-2.mr-3.float-left= icon('globe')
|
|
5
|
+
= organization
|
|
6
|
+
%br
|
|
7
|
+
%small.text-muted
|
|
8
|
+
- if organization.class.try(:effective_memberships_organization_owner?)
|
|
9
|
+
- if (membership = organization.try(:membership)).present?
|
|
10
|
+
= membership.statuses.to_sentence
|
|
11
|
+
= membership.categories.to_sentence
|
|
12
|
+
member
|
|
13
|
+
- if membership.number.present?
|
|
14
|
+
= ' #' + membership.number
|
|
15
|
+
- elsif organization.membership_removed?
|
|
16
|
+
Removed member
|
|
17
|
+
- else
|
|
18
|
+
Not a member
|
|
19
|
+
= ' • '.html_safe
|
|
20
|
+
|
|
21
|
+
= organization.email
|
|
22
|
+
|
|
23
|
+
- if (principals = organization.representatives.select { |rep| rep.is_any?(:principal) }).present?
|
|
24
|
+
= ' • '.html_safe
|
|
25
|
+
= principals.to_sentence
|
|
26
|
+
|
|
27
|
+
= ' • '.html_safe
|
|
28
|
+
= pluralize(organization.representatives.count, 'representative')
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
= render 'admin/search/layout' do
|
|
2
|
+
|
|
3
|
+
= effective_form_with(scope: :q, model: search, method: :get, url: effective_search.admin_search_path) do |f|
|
|
4
|
+
= hidden_field_tag :page, (params[:page] || 1)
|
|
5
|
+
|
|
6
|
+
.d-flex.align-items-center
|
|
7
|
+
.flex-grow-1
|
|
8
|
+
= f.search_field :term, placeholder: 'Search', label: false, autofocus: true, autocomplete: 'off',
|
|
9
|
+
'data-load-ajax-url': effective_search.admin_search_path,
|
|
10
|
+
'data-load-ajax-div': '#effective-search-results',
|
|
11
|
+
'data-load-ajax-all': true
|
|
12
|
+
|
|
13
|
+
- if modal
|
|
14
|
+
%div
|
|
15
|
+
= link_to 'Clear', '#', class: 'btn-link', style: "position: absolute; top: 1.5rem; right: 4rem; z-index: 10;", 'data-effective-search-clear': true
|
|
16
|
+
|
|
17
|
+
%div.ml-4.mb-3
|
|
18
|
+
%button.close{type: 'button', data: { dismiss: 'modal' }, 'aria-label': 'Close'}
|
|
19
|
+
%span{'aria-hidden': 'true'} ×
|
|
20
|
+
|
|
21
|
+
#effective-search-results
|
|
22
|
+
- if search.present?
|
|
23
|
+
- # Users
|
|
24
|
+
%p Users (#{search.users.count})
|
|
25
|
+
%table.table.table-hover
|
|
26
|
+
%tbody= render(collection: search.user_results, partial: 'admin/search/user')
|
|
27
|
+
|
|
28
|
+
- # Organizations
|
|
29
|
+
%p #{ets(search.organizations)} (#{search.organizations.count})
|
|
30
|
+
%table.table.table-hover
|
|
31
|
+
%tbody= render(collection: search.organization_results, partial: 'admin/search/organization')
|
|
32
|
+
|
|
33
|
+
- # Applicants
|
|
34
|
+
%p Applicants (#{search.applicants.count})
|
|
35
|
+
%table.table.table-hover
|
|
36
|
+
%tbody= render(collection: search.applicant_results, partial: 'admin/search/applicant')
|
|
37
|
+
|
|
38
|
+
- # Orders
|
|
39
|
+
%p Orders (#{search.orders.count})
|
|
40
|
+
%table.table.table-hover
|
|
41
|
+
%tbody= render(collection: search.order_results, partial: 'admin/search/order')
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
%tr
|
|
2
|
+
%td
|
|
3
|
+
%a{href: edit_admin_user_path(user), title: user}
|
|
4
|
+
%span.badge.rounded-circle.p-2.mr-3.float-left= icon('user')
|
|
5
|
+
= user.to_s
|
|
6
|
+
%br
|
|
7
|
+
%small.text-muted
|
|
8
|
+
- if(membership = user.try(:membership) || user.try(:organizations).try(:first).try(:membership)).present?
|
|
9
|
+
= membership.statuses.to_sentence
|
|
10
|
+
= membership.categories.to_sentence
|
|
11
|
+
member
|
|
12
|
+
- if membership.number.present?
|
|
13
|
+
= ' #' + membership.number
|
|
14
|
+
- elsif user.membership_removed?
|
|
15
|
+
Removed member
|
|
16
|
+
- else
|
|
17
|
+
Not a member
|
|
18
|
+
= ' • '.html_safe
|
|
19
|
+
|
|
20
|
+
= user.email
|
|
21
|
+
|
|
22
|
+
- if user.try(:representatives).present?
|
|
23
|
+
= ' • '.html_safe
|
|
24
|
+
= user.representatives.map(&:organization).to_sentence
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
= render('admin/search/search', search: @search, modal: false)
|
data/config/routes.rb
CHANGED
data/lib/effective_search.rb
CHANGED
|
@@ -5,7 +5,7 @@ require 'effective_search/version'
|
|
|
5
5
|
module EffectiveSearch
|
|
6
6
|
|
|
7
7
|
def self.config_keys
|
|
8
|
-
[:search_class_name, :search_contents_table_name, :authenticate_user, :layout]
|
|
8
|
+
[:search_class_name, :admin_search_class_name, :search_contents_table_name, :authenticate_user, :layout]
|
|
9
9
|
end
|
|
10
10
|
|
|
11
11
|
include EffectiveGem
|
|
@@ -14,6 +14,10 @@ module EffectiveSearch
|
|
|
14
14
|
search_class_name&.constantize || Effective::Search
|
|
15
15
|
end
|
|
16
16
|
|
|
17
|
+
def self.AdminSearch
|
|
18
|
+
admin_search_class_name&.constantize || Effective::AdminSearch
|
|
19
|
+
end
|
|
20
|
+
|
|
17
21
|
def self.authenticate_user?
|
|
18
22
|
authenticate_user == true
|
|
19
23
|
end
|
metadata
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
|
2
2
|
name: effective_search
|
|
3
3
|
version: !ruby/object:Gem::Version
|
|
4
|
-
version: 0.
|
|
4
|
+
version: 0.3.0
|
|
5
5
|
platform: ruby
|
|
6
6
|
authors:
|
|
7
7
|
- Code and Effect
|
|
8
8
|
autorequire:
|
|
9
9
|
bindir: bin
|
|
10
10
|
cert_chain: []
|
|
11
|
-
date:
|
|
11
|
+
date: 2026-01-20 00:00:00.000000000 Z
|
|
12
12
|
dependencies:
|
|
13
13
|
- !ruby/object:Gem::Dependency
|
|
14
14
|
name: rails
|
|
@@ -162,16 +162,30 @@ files:
|
|
|
162
162
|
- Rakefile
|
|
163
163
|
- app/assets/config/effective_search_manifest.js
|
|
164
164
|
- app/assets/javascripts/effective_search.js
|
|
165
|
+
- app/assets/javascripts/effective_search/admin.js
|
|
165
166
|
- app/assets/javascripts/effective_search/base.js
|
|
166
167
|
- app/assets/stylesheets/effective_search.scss
|
|
168
|
+
- app/assets/stylesheets/effective_search/admin.scss
|
|
167
169
|
- app/assets/stylesheets/effective_search/base.scss
|
|
168
170
|
- app/controllers/admin/search_contents_controller.rb
|
|
171
|
+
- app/controllers/admin/search_controller.rb
|
|
169
172
|
- app/controllers/effective/search_controller.rb
|
|
170
173
|
- app/datatables/admin/effective_search_contents_datatable.rb
|
|
171
174
|
- app/helpers/effective_search_helper.rb
|
|
175
|
+
- app/models/concerns/effective_search_admin_search.rb
|
|
172
176
|
- app/models/concerns/effective_search_search.rb
|
|
177
|
+
- app/models/effective/admin_search.rb
|
|
173
178
|
- app/models/effective/search.rb
|
|
174
179
|
- app/models/effective/search_content.rb
|
|
180
|
+
- app/views/admin/search/_applicant.html.haml
|
|
181
|
+
- app/views/admin/search/_form_nav_icon.html.haml
|
|
182
|
+
- app/views/admin/search/_layout.html.haml
|
|
183
|
+
- app/views/admin/search/_modal.html.haml
|
|
184
|
+
- app/views/admin/search/_order.html.haml
|
|
185
|
+
- app/views/admin/search/_organization.html.haml
|
|
186
|
+
- app/views/admin/search/_search.html.haml
|
|
187
|
+
- app/views/admin/search/_user.html.haml
|
|
188
|
+
- app/views/admin/search/index.html.haml
|
|
175
189
|
- app/views/admin/search_contents/_form.html.haml
|
|
176
190
|
- app/views/admin/search_contents/_form_search_content.html.haml
|
|
177
191
|
- app/views/effective/search/_form.html.haml
|