spotlight_search 0.2.0 → 0.3.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Gemfile.lock +9 -11
- data/README.md +45 -30
- data/app/assets/javascripts/spotlight_search.js +9 -0
- data/app/controllers/spotlight_search/export_jobs_controller.rb +3 -1
- data/app/jobs/spotlight_search/export_job.rb +11 -4
- data/lib/generators/spotlight_search/filter_generator.rb +21 -0
- data/lib/generators/spotlight_search/install_generator.rb +27 -0
- data/lib/generators/spotlight_search/templates/application.css.scss +26 -0
- data/lib/generators/spotlight_search/templates/application.js +19 -0
- data/lib/generators/spotlight_search/templates/coffee.js +6 -0
- data/lib/generators/spotlight_search/templates/controller.rb.erb +91 -0
- data/lib/generators/spotlight_search/templates/environment.js +20 -0
- data/lib/generators/spotlight_search/templates/filters.html.erb +10 -0
- data/lib/generators/spotlight_search/templates/scaffolds.coffee +11 -0
- data/lib/generators/spotlight_search/templates/spotlight_search.rb +5 -0
- data/lib/generators/spotlight_search/templates/webpacker.yml +102 -0
- data/lib/generators/spotlight_search/templates/webpacker_gem_assets.rb +55 -0
- data/lib/spotlight_search/exportable_columns_v2.rb +5 -0
- data/lib/spotlight_search/helpers.rb +60 -14
- data/lib/spotlight_search/version.rb +1 -1
- data/spotlight_search.gemspec +2 -3
- metadata +22 -24
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: '099ecfc87e93555a16a5e494f3b88f69cbe8688c967444b5fac831dc62b5b650'
|
4
|
+
data.tar.gz: 5d3853b623df2045aadb98b70f69dfd9faf9b8d0878a4cb080bdeccaf6f5f396
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 651ec6f74e9c8d4bae5678be1636a157e13fa2420446ce50e4f0f7d68a438c0247d3f682f1759aedeed5d99095e5bd4a39957e99cad62ea38d747ad17dd8a068
|
7
|
+
data.tar.gz: 909f5d9e9a2472bd63d4d7db9ecd77e9c0007f4b10072eb1106ff7f411b45bf103c47de44dd9e03c1c64d8099bf85aa5cd21780287881036a5c4880ce03a7ab4
|
data/Gemfile.lock
CHANGED
@@ -2,8 +2,7 @@ PATH
|
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
4
|
spotlight_search (0.1.9)
|
5
|
-
|
6
|
-
zip-zip
|
5
|
+
caxlsx (~> 3.0.1)
|
7
6
|
|
8
7
|
GEM
|
9
8
|
remote: https://rubygems.org/
|
@@ -50,11 +49,12 @@ GEM
|
|
50
49
|
minitest (~> 5.1)
|
51
50
|
tzinfo (~> 1.1)
|
52
51
|
arel (9.0.0)
|
53
|
-
axlsx (2.0.1)
|
54
|
-
htmlentities (~> 4.3.1)
|
55
|
-
nokogiri (>= 1.4.1)
|
56
|
-
rubyzip (~> 1.0.0)
|
57
52
|
builder (3.2.4)
|
53
|
+
caxlsx (3.0.1)
|
54
|
+
htmlentities (~> 4.3, >= 4.3.4)
|
55
|
+
mimemagic (~> 0.3)
|
56
|
+
nokogiri (~> 1.10, >= 1.10.4)
|
57
|
+
rubyzip (>= 1.3.0, < 3)
|
58
58
|
concurrent-ruby (1.1.6)
|
59
59
|
crass (1.0.6)
|
60
60
|
diff-lcs (1.3)
|
@@ -77,7 +77,7 @@ GEM
|
|
77
77
|
mini_portile2 (2.4.0)
|
78
78
|
minitest (5.14.0)
|
79
79
|
nio4r (2.5.2)
|
80
|
-
nokogiri (1.10.
|
80
|
+
nokogiri (1.10.9)
|
81
81
|
mini_portile2 (~> 2.4.0)
|
82
82
|
rack (2.2.2)
|
83
83
|
rack-test (1.1.0)
|
@@ -120,7 +120,7 @@ GEM
|
|
120
120
|
diff-lcs (>= 1.2.0, < 2.0)
|
121
121
|
rspec-support (~> 3.8.0)
|
122
122
|
rspec-support (3.8.0)
|
123
|
-
rubyzip (
|
123
|
+
rubyzip (2.3.0)
|
124
124
|
sprockets (4.0.0)
|
125
125
|
concurrent-ruby (~> 1.0)
|
126
126
|
rack (> 1, < 3)
|
@@ -134,9 +134,7 @@ GEM
|
|
134
134
|
thread_safe (~> 0.1)
|
135
135
|
websocket-driver (0.7.1)
|
136
136
|
websocket-extensions (>= 0.1.0)
|
137
|
-
websocket-extensions (0.1.
|
138
|
-
zip-zip (0.3)
|
139
|
-
rubyzip (>= 1.0.0)
|
137
|
+
websocket-extensions (0.1.5)
|
140
138
|
|
141
139
|
PLATFORMS
|
142
140
|
ruby
|
data/README.md
CHANGED
@@ -4,6 +4,13 @@
|
|
4
4
|
|
5
5
|
It helps filtering, sorting and exporting tables easier.
|
6
6
|
|
7
|
+
First create a new rails project with the following command. If you are adding to existing project skip this
|
8
|
+
|
9
|
+
```
|
10
|
+
rails new blog -m https://raw.githubusercontent.com/commutatus/cm-rails-template/devise_integration/template.rb
|
11
|
+
```
|
12
|
+
|
13
|
+
|
7
14
|
## Installation
|
8
15
|
|
9
16
|
Add this line to your application's Gemfile:
|
@@ -20,7 +27,33 @@ Or install it manually:
|
|
20
27
|
|
21
28
|
$ gem install spotlight_search
|
22
29
|
|
23
|
-
|
30
|
+
Generator that installs mandatory files and gems to application
|
31
|
+
|
32
|
+
$ rails g spotlight_search:install
|
33
|
+
|
34
|
+
The install generator does the following
|
35
|
+
|
36
|
+
* `require spotlight_search` added to application.js
|
37
|
+
|
38
|
+
* Copies required files for the spotlight_search to work, Such as gemassets.rb, webpacker.yml, environment.js
|
39
|
+
|
40
|
+
* Copies initializer file
|
41
|
+
|
42
|
+
* Adds a line in route for mounting.
|
43
|
+
|
44
|
+
Generator that installs filter and table files to application
|
45
|
+
$ rails g spotlight_search filter orders --filters scope_name:filter_type
|
46
|
+
$ rails g spotlight_search filter orders --filters search:input order_status:multi_select status:select
|
47
|
+
|
48
|
+
scope_name is the model scope name, scope can written after running this generator, it won't throw any error that it has to be present.
|
49
|
+
|
50
|
+
Following filter type are supported
|
51
|
+
* input
|
52
|
+
* single-select
|
53
|
+
* multi-select
|
54
|
+
* datetime
|
55
|
+
* daterange
|
56
|
+
|
24
57
|
|
25
58
|
## Usage
|
26
59
|
|
@@ -66,24 +99,20 @@ end
|
|
66
99
|
#### View
|
67
100
|
Please note that the below code is in haml.
|
68
101
|
|
69
|
-
**STEP - 1
|
70
|
-
|
71
|
-
First step is to add the input box to search. Here there are few elements that should be placed mandatorily.
|
102
|
+
**STEP - 1 Filters**
|
72
103
|
|
73
|
-
|
74
|
-
.filters.w-100 data-filter-url="/admin/workshops" data-replacement-class="workshops_table"
|
75
|
-
.col-md-4.input-group.search
|
76
|
-
input#workshop-search-filter.form-control.filter-box name=("search_term_for_workshops ") placeholder=("Search Workshops") type="text" data-behaviour="filter" data-scope="search" data-type="input-filter"
|
77
|
-
```
|
104
|
+
**Filter Wrapper, Select-tags and Inputs**
|
78
105
|
|
79
|
-
|
106
|
+
| Generator | *Mandatory(Data Attributes, Select options) | *Optional(Classes, Placeholders) |
|
107
|
+
|----------------------------------------------------------------------------------|------------------------------------------------------------------------------------------------------------------------|----------------------------------------------------------------------------------------|
|
108
|
+
| `= filter_wrapper(data_behaviours, classes=nil)` | `{filter_url: '/users', replacement_class: 'users-table'}` | "filter-classes" |
|
109
|
+
| | | |
|
110
|
+
| `= cm_select_tag(select_options, data_behaviours, classes=nil, placeholder=nil)` | `{behaviour: "filter", scope: "status", type: "select-filter}` , User.all.map {\|user\| [user.name.titleize, user.id]} | `"user-select"`, placeholder = `"Users"` |
|
111
|
+
| | | |
|
112
|
+
| `= cm_textfield_tag(data_behaviours, classes=nil, placeholder=nil)` | `{behaviour: "filter", scope: "search", type: "input-filter}` | `"user-search"`, placeholder = `"Search"` |
|
113
|
+
| | | |
|
114
|
+
| `= clear_filters(clear_path, classes=nil, data_behaviours=nil, clear_text=nil)` | clear_path = `users_path` | `"clear-filter"`, data_behaviours = `{behaviour: 'clear'}`, clear_text = `"Clear all"` |
|
80
115
|
|
81
|
-
* `.filters` All search input / select filter should be nested inside this class name.
|
82
|
-
* `data-filter-url` Is mandatory, this is the search URL, Mostly this will hit the index action.
|
83
|
-
* `data-replacement-class` After ajax this is the class name where the data will get appended.
|
84
|
-
* `data-behaviour="filter"` If the input behaviour is set to filter then this will get added to ajax
|
85
|
-
* `data-scope="search"` This is the model scope name, The helper method will call this when filter is applied.
|
86
|
-
* `data-type="input-filter"` This is to tell if the element is input or select other value is `data-type="select-filter"`
|
87
116
|
|
88
117
|
**STEP - 2 Pagination**
|
89
118
|
|
@@ -105,20 +134,6 @@ th = sortable "name", "Name", @filtered_result.sort[:sort_column], @filtered_res
|
|
105
134
|
|
106
135
|
You will need to have a background job processor such as `sidekiq`, `resque`, `delayed_job` etc as the file will be generated in the background and will be sent to the email passed. If you need to use any other service for sending emails, you will need to override `ExportMailer` class.
|
107
136
|
|
108
|
-
#### Initializer
|
109
|
-
An initializer will have to be created to extend the functionality to ActiveRecord.
|
110
|
-
|
111
|
-
```ruby
|
112
|
-
# config/initializers/spotlight_search.rb
|
113
|
-
ActiveRecord::Base.include SpotlightSearch::ExportableColumns
|
114
|
-
```
|
115
|
-
|
116
|
-
#### Routes
|
117
|
-
A line has to be added to the routes.
|
118
|
-
|
119
|
-
```ruby
|
120
|
-
mount SpotlightSearch::Engine => '/spotlight_search'
|
121
|
-
```
|
122
137
|
|
123
138
|
#### <a name="export-view"></a>View
|
124
139
|
|
@@ -74,6 +74,15 @@ $(document).on('keyup', '[data-type="input-filter"]', function() {
|
|
74
74
|
});
|
75
75
|
});
|
76
76
|
|
77
|
+
$(document).on('change', '[data-type="range-filter"]', function() {
|
78
|
+
var thisObj;
|
79
|
+
thisObj = $(this);
|
80
|
+
return $(function() {
|
81
|
+
return get_paginated_list(1, thisObj);
|
82
|
+
});
|
83
|
+
});
|
84
|
+
|
85
|
+
|
77
86
|
$(document).on('change', '[data-type="select-filter"]', function() {
|
78
87
|
var thisObj;
|
79
88
|
thisObj = $(this);
|
@@ -4,7 +4,9 @@ module SpotlightSearch
|
|
4
4
|
begin
|
5
5
|
klass = params[:class_name].constantize
|
6
6
|
if klass.validate_exportable_columns(params[:columns])
|
7
|
-
|
7
|
+
(filter_params = params[:filters].permit!) if params[:filters].present?
|
8
|
+
(sort_params = params[:sort].permit!) if params[:sort].present?
|
9
|
+
ExportJob.perform_later(klass.name, params[:email], params[:columns], filter_params, sort_params)
|
8
10
|
notice = 'Successfully queued for export'
|
9
11
|
else
|
10
12
|
notice = 'Invalid columns found'
|
@@ -1,4 +1,4 @@
|
|
1
|
-
require '
|
1
|
+
require 'caxlsx'
|
2
2
|
|
3
3
|
module SpotlightSearch
|
4
4
|
class ExportJob < ApplicationJob
|
@@ -15,7 +15,7 @@ module SpotlightSearch
|
|
15
15
|
subject = "#{klass.name} export at #{Time.now}"
|
16
16
|
ExportMailer.send_excel_file(email, file_path, subject).deliver_now
|
17
17
|
File.delete(file_path)
|
18
|
-
rescue
|
18
|
+
rescue StandardError => e
|
19
19
|
ExportMailer.send_error_message(email, e).deliver_now
|
20
20
|
end
|
21
21
|
|
@@ -26,6 +26,11 @@ module SpotlightSearch
|
|
26
26
|
records = records.send(scope, scope_args)
|
27
27
|
end
|
28
28
|
end
|
29
|
+
if klass.default_filters.present?
|
30
|
+
klass.default_filters.each do |scope|
|
31
|
+
records = records.send(scope)
|
32
|
+
end
|
33
|
+
end
|
29
34
|
if sort.present?
|
30
35
|
records = records.order("#{sort['sort_column']} #{sort['sort_direction']}")
|
31
36
|
end
|
@@ -37,7 +42,9 @@ module SpotlightSearch
|
|
37
42
|
columns = columns.map(&:to_sym)
|
38
43
|
records.select(*columns)
|
39
44
|
when :v2
|
40
|
-
|
45
|
+
deserialized_columns = SpotlightSearch::Utils.deserialize_csv_columns(columns, :as_json_params)
|
46
|
+
# This includes isn't recursve, a full solution should be recursive
|
47
|
+
records.includes(deserialized_columns[:include].keys).find_each.as_json(deserialized_columns)
|
41
48
|
end
|
42
49
|
end
|
43
50
|
|
@@ -66,7 +73,7 @@ module SpotlightSearch
|
|
66
73
|
columns.size.times { size_arr << 22 }
|
67
74
|
xl = Axlsx::Package.new
|
68
75
|
xl.workbook.add_worksheet do |sheet|
|
69
|
-
sheet.add_row columns, b: true
|
76
|
+
sheet.add_row columns&.map(&:titleize), b: true
|
70
77
|
flattened_records.each do |record|
|
71
78
|
sheet.add_row(columns.map { |column| record[column] })
|
72
79
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module SpotlightSearch
|
2
|
+
module Generators
|
3
|
+
class FilterGenerator < Rails::Generators::Base
|
4
|
+
source_root File.expand_path('templates', __dir__)
|
5
|
+
|
6
|
+
argument :model, type: :string, required: true, desc: "Pass a model name"
|
7
|
+
class_option :filters, aliases: "-f", type: :array, desc: "Pass filters and scopes as an array. E.x: date_filter:datetime search:input"
|
8
|
+
|
9
|
+
def copy_filter_contents_to_app
|
10
|
+
if @options.filters?
|
11
|
+
inject_into_file "app/models/#{model.singularize}.rb", after: "ApplicationRecord" do
|
12
|
+
"\n include SpotlightSearch\n"
|
13
|
+
end
|
14
|
+
template 'filters.html.erb', "app/views/admin/#{model}/_filters.html.slim"
|
15
|
+
template 'controller.rb.erb', "app/controllers/admin/#{model}_controller.rb"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'yaml'
|
2
|
+
|
3
|
+
module SpotlightSearch
|
4
|
+
module Generators
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
6
|
+
source_root File.expand_path('templates', __dir__)
|
7
|
+
|
8
|
+
def copy_webpacker_gem_assets
|
9
|
+
copy_file 'webpacker_gem_assets.rb', 'config/initializers/webpacker_gem_assets.rb'
|
10
|
+
copy_file 'webpacker.yml', 'config/webpacker.yml'
|
11
|
+
copy_file 'environment.js', 'config/webpack/environment.js'
|
12
|
+
copy_file 'coffee.js', 'config/webpack/loaders/coffee.js'
|
13
|
+
copy_file 'spotlight_search.rb', 'config/initializers/spotlight_search.rb'
|
14
|
+
copy_file 'application.css.scss', 'app/assets/stylesheets/application.css.scss'
|
15
|
+
route "mount SpotlightSearch::Engine => '/spotlight_search'"
|
16
|
+
end
|
17
|
+
|
18
|
+
def add_essentials
|
19
|
+
system("yarn add jquery coffeescript coffee-loader select2 popper.js daterangepicker bootstrap-datepicker")
|
20
|
+
template "application.js", "app/javascript/packs/application.js"
|
21
|
+
template 'scaffolds.coffee', "app/javascript/application/coffee_scripts/scaffolds.coffee"
|
22
|
+
gem 'kaminari'
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
/*
|
2
|
+
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
3
|
+
* listed below.
|
4
|
+
*
|
5
|
+
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, or any plugin's
|
6
|
+
* vendor/assets/stylesheets directory can be referenced here using a relative path.
|
7
|
+
*
|
8
|
+
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
9
|
+
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
10
|
+
* files in this directory. Styles in this file should be added after the last require_* statement.
|
11
|
+
* It is generally better to create a new file per style scope.
|
12
|
+
*
|
13
|
+
*= require_self
|
14
|
+
*= require font-awesome
|
15
|
+
*= require 'select2'
|
16
|
+
*= require bootstrap/dist/css/bootstrap
|
17
|
+
*= require bootstrap-datepicker/dist/css/bootstrap-datepicker3
|
18
|
+
*= require daterangepicker
|
19
|
+
*/
|
20
|
+
|
21
|
+
$fa-font-path: '@fortawesome/fontawesome-free/webfonts';
|
22
|
+
@import '@fortawesome/fontawesome-free/scss/fontawesome';
|
23
|
+
@import '@fortawesome/fontawesome-free/scss/solid';
|
24
|
+
@import '@fortawesome/fontawesome-free/scss/regular';
|
25
|
+
@import '@fortawesome/fontawesome-free/scss/brands';
|
26
|
+
@import '@fortawesome/fontawesome-free/scss/v4-shims';
|
@@ -0,0 +1,19 @@
|
|
1
|
+
// This file is automatically compiled by Webpack, along with any other files
|
2
|
+
// present in this directory. You're encouraged to place your actual application logic in
|
3
|
+
// a relevant structure within app/javascript and only use these pack files to reference
|
4
|
+
// that code so it'll be compiled.
|
5
|
+
|
6
|
+
|
7
|
+
require("@rails/ujs").start()
|
8
|
+
require("turbolinks").start()
|
9
|
+
require("@rails/activestorage").start()
|
10
|
+
require("spotlight_search")
|
11
|
+
require("daterangepicker")
|
12
|
+
|
13
|
+
import jQuery from 'jquery';
|
14
|
+
window.$ = jQuery
|
15
|
+
window.jQuery = jQuery
|
16
|
+
import 'select2';
|
17
|
+
import 'bootstrap/dist/js/bootstrap'
|
18
|
+
import 'bootstrap-datepicker/dist/js/bootstrap-datepicker.js';
|
19
|
+
import '../application/coffee_scripts/scaffolds.coffee';
|
@@ -0,0 +1,91 @@
|
|
1
|
+
class Admin::<%= model.titleize.gsub(' ', '') %>Controller < ApplicationController
|
2
|
+
before_action :set_<%= model.singularize %>, only: [:show, :edit, :update, :destroy]
|
3
|
+
|
4
|
+
# GET /<%= model %>
|
5
|
+
# GET /<%= model %>.json
|
6
|
+
def index
|
7
|
+
@filtered_result = <%= model.classify %>.all.filter_by(params[:page], filter_params.to_h, sort_params.to_h)
|
8
|
+
respond_to do |format|
|
9
|
+
if request.xhr?
|
10
|
+
format.html { render partial: 'table' }
|
11
|
+
format.json
|
12
|
+
else
|
13
|
+
format.html
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
# GET /<%= model %>/1
|
19
|
+
# GET /<%= model %>/1.json
|
20
|
+
def show
|
21
|
+
end
|
22
|
+
|
23
|
+
# GET /<%= model %>/new
|
24
|
+
def new
|
25
|
+
@<%= model.singularize %> = <%= model.singularize.titleize.gsub(' ', '') %>.new
|
26
|
+
end
|
27
|
+
|
28
|
+
# GET /<%= model %>/1/edit
|
29
|
+
def edit
|
30
|
+
end
|
31
|
+
|
32
|
+
# POST /<%= model %>
|
33
|
+
# POST /<%= model %>.json
|
34
|
+
def create
|
35
|
+
@<%= model.singularize %> = <%= model.singularize.titleize.gsub(' ', '') %>.new(<%= model.singularize %>_params)
|
36
|
+
|
37
|
+
respond_to do |format|
|
38
|
+
if @<%= model.singularize %>.save
|
39
|
+
format.html { redirect_to admin_<%= model.singularize %>_path(@<%= model.singularize %>), notice: '<%= model.singularize.titleize.gsub(' ', '') %> was successfully created.' }
|
40
|
+
format.json { render :show, status: :created, location: @<%= model.singularize %> }
|
41
|
+
else
|
42
|
+
format.html { render :new }
|
43
|
+
format.json { render json: @<%= model.singularize %>.errors, status: :unprocessable_entity }
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
# PATCH/PUT /<%= model %>/1
|
49
|
+
# PATCH/PUT /<%= model %>/1.json
|
50
|
+
def update
|
51
|
+
respond_to do |format|
|
52
|
+
if @<%= model.singularize %>.update(<%= model.singularize %>_params)
|
53
|
+
format.html { redirect_to admin_<%= model.singularize %>_path(@<%= model.singularize %>), notice: '<%= model.singularize.titleize.gsub(' ', '') %> was successfully updated.' }
|
54
|
+
format.json { render :show, status: :ok, location: @<%= model.singularize %> }
|
55
|
+
else
|
56
|
+
format.html { render :edit }
|
57
|
+
format.json { render json: @<%= model.singularize %>.errors, status: :unprocessable_entity }
|
58
|
+
end
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
# DELETE /<%= model %>/1
|
63
|
+
# DELETE /<%= model %>/1.json
|
64
|
+
def destroy
|
65
|
+
@<%= model.singularize %>.destroy
|
66
|
+
respond_to do |format|
|
67
|
+
format.html { redirect_to admin_<%= model %>_path, notice: '<%= model.singularize.titleize.gsub(' ', '') %> was successfully destroyed.' }
|
68
|
+
format.json { head :no_content }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
# Use callbacks to share common setup or constraints between actions.
|
75
|
+
def set_<%= model.singularize %>
|
76
|
+
@<%= model.singularize %> = <%= model.singularize.titleize.gsub(' ', '') %>.find(params[:id])
|
77
|
+
end
|
78
|
+
|
79
|
+
# Only allow a list of trusted parameters through.
|
80
|
+
def <%= model.singularize %>_params
|
81
|
+
params.require(:<%= model.singularize %>).permit()
|
82
|
+
end
|
83
|
+
|
84
|
+
def filter_params
|
85
|
+
params.require(:filters).permit(<%= @options.filters.map{ |f| ':' + f.split(':')[0] }.join(', ') %>) if params[:filters]
|
86
|
+
end
|
87
|
+
|
88
|
+
def sort_params
|
89
|
+
params.require(:sort).permit(:sort_column, :sort_direction) if params[:sort]
|
90
|
+
end
|
91
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
const { environment } = require('@rails/webpacker')
|
2
|
+
const coffee = require('./loaders/coffee')
|
3
|
+
|
4
|
+
const webpack = require('webpack')
|
5
|
+
environment.plugins.prepend('Provide',
|
6
|
+
new webpack.ProvidePlugin({
|
7
|
+
$: require.resolve('jquery'),
|
8
|
+
jQuery: require.resolve('jquery'),
|
9
|
+
Popper: ['popper.js', 'default']
|
10
|
+
})
|
11
|
+
)
|
12
|
+
|
13
|
+
const { add_paths_to_environment } = require(
|
14
|
+
// `${environment.plugins.get('Environment').defaultValues["PWD"]}/config/environments/_add_gem_paths`
|
15
|
+
`/tmp/_add_gem_paths`
|
16
|
+
)
|
17
|
+
add_paths_to_environment(environment)
|
18
|
+
|
19
|
+
environment.loaders.prepend('coffee', coffee)
|
20
|
+
module.exports = environment
|
@@ -0,0 +1,10 @@
|
|
1
|
+
.filter-section
|
2
|
+
.filter-section__lhs
|
3
|
+
= filter_wrapper({filter_url: '/admin/<%= model %>', replacement_class: "<%= model %>-table"})
|
4
|
+
<%- @options.filters.each do |filter| -%>
|
5
|
+
<%- filter_arr = filter.split(':') -%>
|
6
|
+
= cm_filter_tag("<%= filter_arr[1] %>", "<%= filter_arr[0] %>", "", 'field-area', "<%= filter_arr[0].titleize %>")
|
7
|
+
<%- end -%>
|
8
|
+
= clear_filters(admin_<%= model %>_path, nil, nil, "Clear all")
|
9
|
+
.filter-section__rhs
|
10
|
+
= link_to 'Add', <%= "new_admin_" + model.singularize + "_path" %>, class: 'filter-btn'
|
@@ -0,0 +1,11 @@
|
|
1
|
+
$(document).on "turbolinks:load", () ->
|
2
|
+
$('.select2-multiple').select2()
|
3
|
+
$('.select2-single').select2(
|
4
|
+
allowClear: true
|
5
|
+
)
|
6
|
+
$('.filter-rangepicker').daterangepicker()
|
7
|
+
$('.datepicker').datepicker({
|
8
|
+
format: 'dd/mm/yyyy',
|
9
|
+
todayHighlight: true,
|
10
|
+
autoclose: true,
|
11
|
+
})
|
@@ -0,0 +1,102 @@
|
|
1
|
+
# Note: You must restart bin/webpack-dev-server for changes to take effect
|
2
|
+
|
3
|
+
default: &default
|
4
|
+
source_path: app/javascript
|
5
|
+
source_entry_path: packs
|
6
|
+
public_root_path: public
|
7
|
+
public_output_path: packs
|
8
|
+
cache_path: tmp/cache/webpacker
|
9
|
+
check_yarn_integrity: false
|
10
|
+
webpack_compile_output: true
|
11
|
+
|
12
|
+
# Additional paths webpack should lookup modules
|
13
|
+
# ['app/assets', 'engine/foo/app/assets']
|
14
|
+
resolved_paths: []
|
15
|
+
resolved_gems_output_path: '/tmp/_add_gem_paths.js'
|
16
|
+
resolved_gems: [
|
17
|
+
'spotlight_search'
|
18
|
+
]
|
19
|
+
|
20
|
+
# Reload manifest.json on all requests so we reload latest compiled packs
|
21
|
+
cache_manifest: false
|
22
|
+
|
23
|
+
# Extract and emit a css file
|
24
|
+
extract_css: false
|
25
|
+
|
26
|
+
static_assets_extensions:
|
27
|
+
- .jpg
|
28
|
+
- .jpeg
|
29
|
+
- .png
|
30
|
+
- .gif
|
31
|
+
- .tiff
|
32
|
+
- .ico
|
33
|
+
- .svg
|
34
|
+
- .eot
|
35
|
+
- .otf
|
36
|
+
- .ttf
|
37
|
+
- .woff
|
38
|
+
- .woff2
|
39
|
+
|
40
|
+
extensions:
|
41
|
+
- .coffee
|
42
|
+
- .mjs
|
43
|
+
- .js
|
44
|
+
- .sass
|
45
|
+
- .scss
|
46
|
+
- .css
|
47
|
+
- .module.sass
|
48
|
+
- .module.scss
|
49
|
+
- .module.css
|
50
|
+
- .png
|
51
|
+
- .svg
|
52
|
+
- .gif
|
53
|
+
- .jpeg
|
54
|
+
- .jpg
|
55
|
+
|
56
|
+
development:
|
57
|
+
<<: *default
|
58
|
+
compile: true
|
59
|
+
|
60
|
+
# Verifies that correct packages and versions are installed by inspecting package.json, yarn.lock, and node_modules
|
61
|
+
check_yarn_integrity: true
|
62
|
+
|
63
|
+
# Reference: https://webpack.js.org/configuration/dev-server/
|
64
|
+
dev_server:
|
65
|
+
https: false
|
66
|
+
host: localhost
|
67
|
+
port: 3035
|
68
|
+
public: localhost:3035
|
69
|
+
hmr: false
|
70
|
+
# Inline should be set to true if using HMR
|
71
|
+
inline: true
|
72
|
+
overlay: true
|
73
|
+
compress: true
|
74
|
+
disable_host_check: true
|
75
|
+
use_local_ip: false
|
76
|
+
quiet: false
|
77
|
+
pretty: false
|
78
|
+
headers:
|
79
|
+
'Access-Control-Allow-Origin': '*'
|
80
|
+
watch_options:
|
81
|
+
ignored: '**/node_modules/**'
|
82
|
+
|
83
|
+
|
84
|
+
test:
|
85
|
+
<<: *default
|
86
|
+
compile: true
|
87
|
+
|
88
|
+
# Compile test packs to a separate directory
|
89
|
+
public_output_path: packs-test
|
90
|
+
|
91
|
+
production:
|
92
|
+
<<: *default
|
93
|
+
webpack_compile_output: true
|
94
|
+
|
95
|
+
# Production depends on precompilation of packs prior to booting for performance.
|
96
|
+
compile: false
|
97
|
+
|
98
|
+
# Extract and emit a css file
|
99
|
+
extract_css: true
|
100
|
+
|
101
|
+
# Cache manifest.json for performance
|
102
|
+
cache_manifest: true
|
@@ -0,0 +1,55 @@
|
|
1
|
+
# config/initializers/webpacker_gem_assets.rb
|
2
|
+
|
3
|
+
def default_assets_path
|
4
|
+
'app/assets/javascripts'
|
5
|
+
end
|
6
|
+
|
7
|
+
def output_path
|
8
|
+
Webpacker.config.send(:data)[:resolved_gems_output_path] || '/tmp/_add_gem_paths.js'
|
9
|
+
end
|
10
|
+
|
11
|
+
def resolve_gem_path(gem)
|
12
|
+
if gem.present?
|
13
|
+
gem_path = Gem.loaded_specs[gem]&.full_gem_path
|
14
|
+
if gem_path.present?
|
15
|
+
return "#{gem_path}/#{default_assets_path}"
|
16
|
+
end
|
17
|
+
end
|
18
|
+
abort("Gem '#{gem}' not found, please check webpacker config (#{Webpacker.config.config_path})")
|
19
|
+
end
|
20
|
+
|
21
|
+
buffer = []
|
22
|
+
buffer << "
|
23
|
+
/*
|
24
|
+
* THIS IS A GENERATED FILE.
|
25
|
+
* DO NOT CHECK IT INTO SOURCE CONTROL
|
26
|
+
*/
|
27
|
+
function add_paths_to_environment(environment) {".strip
|
28
|
+
|
29
|
+
resolved_gems = Webpacker.config.send(:data)[:resolved_gems]
|
30
|
+
if resolved_gems.any?
|
31
|
+
buffer << "\n environment.resolvedModules.add(\n"
|
32
|
+
buffer << resolved_gems.map do |gem|
|
33
|
+
" { key: 'gem-#{gem}', value: '#{resolve_gem_path(gem)}' }"
|
34
|
+
end.join(",\n")
|
35
|
+
buffer << "\n )\n"
|
36
|
+
end
|
37
|
+
|
38
|
+
buffer << "}\nexports.add_paths_to_environment = add_paths_to_environment\n"
|
39
|
+
|
40
|
+
File.write(
|
41
|
+
output_path,
|
42
|
+
buffer.join
|
43
|
+
)
|
44
|
+
|
45
|
+
module WebpackerGemAssets
|
46
|
+
def resolved_paths
|
47
|
+
self.send(:data)[:resolved_gems].map do |gem|
|
48
|
+
resolve_gem_path gem
|
49
|
+
end.compact.concat(super)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
class Webpacker::Configuration
|
54
|
+
prepend WebpackerGemAssets
|
55
|
+
end
|
@@ -32,6 +32,10 @@ module SpotlightSearch
|
|
32
32
|
self.enabled_columns = [*record_fields, **associated_fields]
|
33
33
|
end
|
34
34
|
|
35
|
+
def default_scopes_for_export(*filter_scopes)
|
36
|
+
self.default_filters = filter_scopes
|
37
|
+
end
|
38
|
+
|
35
39
|
def _model_exportable_columns(klass, *record_fields, **associated_fields)
|
36
40
|
# Gets all the valid columns of a model
|
37
41
|
# If any column is invalid, it also returns it
|
@@ -85,6 +89,7 @@ module SpotlightSearch
|
|
85
89
|
|
86
90
|
included do
|
87
91
|
class_attribute :enabled_columns, instance_accessor: false, default: nil
|
92
|
+
class_attribute :default_filters, instance_accessor: false, default: nil
|
88
93
|
end
|
89
94
|
end
|
90
95
|
end
|
@@ -12,26 +12,26 @@ module SpotlightSearch
|
|
12
12
|
end
|
13
13
|
|
14
14
|
def exportable(email, klass, html_class: [])
|
15
|
-
tag.
|
16
|
-
concat tag.
|
17
|
-
concat
|
15
|
+
tag.a "Export as excel", class: html_class.append("filter-btn modal-btn mr-2"), data: {toggle: "modal", target: "#exportmodal"} do
|
16
|
+
concat tag.i class: 'fa fa-download'
|
17
|
+
concat tag.span " Excel"
|
18
18
|
end
|
19
19
|
end
|
20
20
|
|
21
|
-
def column_pop_up(email, klass)
|
21
|
+
def column_pop_up(email, klass, required_filters = nil)
|
22
22
|
tag.div class: "modal fade", id: "exportmodal", tabindex: "-1", role: "dialog", aria: {labelledby: "exportModal"} do
|
23
23
|
tag.div class: "modal-dialog modal-lg", role: "document" do
|
24
24
|
tag.div class: "modal-content" do
|
25
|
-
concat pop_ups(email, klass)
|
25
|
+
concat pop_ups(email, klass, required_filters)
|
26
26
|
end
|
27
27
|
end
|
28
28
|
end
|
29
29
|
end
|
30
30
|
|
31
|
-
def pop_ups(email, klass)
|
31
|
+
def pop_ups(email, klass, required_filters)
|
32
32
|
tag.div do
|
33
33
|
concat pop_up_header
|
34
|
-
concat pop_up_body(email, klass)
|
34
|
+
concat pop_up_body(email, klass, required_filters)
|
35
35
|
end
|
36
36
|
end
|
37
37
|
|
@@ -44,11 +44,12 @@ module SpotlightSearch
|
|
44
44
|
end
|
45
45
|
end
|
46
46
|
|
47
|
-
def pop_up_body(email, klass)
|
47
|
+
def pop_up_body(email, klass, required_filters)
|
48
48
|
tag.div class: "modal-body" do
|
49
49
|
form_tag '/spotlight_search/export_to_file', id: 'export-to-file-form', style: "width: 100%;", class:"spotlight-csv-export-form" do
|
50
50
|
concat hidden_field_tag 'email', email, id: 'export-to-file-email'
|
51
51
|
concat hidden_field_tag 'class_name', klass.to_s, id: 'export-to-file-klass'
|
52
|
+
filters_to_post_helper(required_filters) if required_filters
|
52
53
|
params_to_post_helper(filters: controller.filter_params) if controller.filter_params
|
53
54
|
params_to_post_helper(sort: controller.sort_params) if controller.sort_params
|
54
55
|
case SpotlightSearch.exportable_columns_version
|
@@ -57,11 +58,18 @@ module SpotlightSearch
|
|
57
58
|
when :v2
|
58
59
|
concat checkbox_row_v2(klass)
|
59
60
|
end
|
60
|
-
concat
|
61
|
+
concat tag.hr
|
62
|
+
concat submit_tag 'Export as excel', class: 'btn btn-primary btn-bordered export-to-file-btn'
|
61
63
|
end
|
62
64
|
end
|
63
65
|
end
|
64
66
|
|
67
|
+
def filters_to_post_helper(required_filters)
|
68
|
+
URI.decode_www_form(required_filters.to_param).each do |param|
|
69
|
+
concat hidden_field_tag "filters[#{param[0]}]", param[1]
|
70
|
+
end
|
71
|
+
end
|
72
|
+
|
65
73
|
def params_to_post_helper(params)
|
66
74
|
URI.decode_www_form(params.to_param).each do |param|
|
67
75
|
concat hidden_field_tag param[0], param[1]
|
@@ -83,6 +91,44 @@ module SpotlightSearch
|
|
83
91
|
end
|
84
92
|
end
|
85
93
|
|
94
|
+
def filter_wrapper(data_behaviours, classes=nil)
|
95
|
+
tag.div class: "filter-wrapper d-flex filters #{classes}", data: data_behaviours do
|
96
|
+
yield
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def cm_filter_tag(input_type, scope_name, value, classes = nil, placeholder = nil)
|
101
|
+
case input_type
|
102
|
+
when 'input'
|
103
|
+
tag.div class: 'filter-field' do
|
104
|
+
concat text_field_tag scope_name, '', class: "#{classes}", data: {behaviour: "filter", scope: scope_name, type: "input-filter"}, placeholder: "#{placeholder}"
|
105
|
+
concat tag.span class: 'fa fa-search search-icon'
|
106
|
+
end
|
107
|
+
when 'single-select'
|
108
|
+
tag.div class: 'filter-field' do
|
109
|
+
select_tag scope_name, options_for_select(value), class: "#{classes} select2-single", data: {behaviour: "filter", scope: scope_name, type: "select-filter"}, include_blank: "#{placeholder}"
|
110
|
+
end
|
111
|
+
when 'multi-select'
|
112
|
+
tag.div class: 'filter-field' do
|
113
|
+
select_tag scope_name, options_for_select(value), class: "#{classes} select2-single", data: {behaviour: "filter", scope: scope_name, type: "select-filter"}, include_blank: "#{placeholder}", multiple: true
|
114
|
+
end
|
115
|
+
when 'datetime'
|
116
|
+
tag.div class: 'filter-field' do
|
117
|
+
concat text_field_tag scope_name, '', class: "#{classes}", data: {behaviour: "filter", scope: scope_name, type: "input-filter", provide: "datepicker"}, placeholder: "#{placeholder}"
|
118
|
+
concat tag.span class: 'fa fa-search search-icon'
|
119
|
+
end
|
120
|
+
when 'daterange'
|
121
|
+
tag.div class: 'filter-field' do
|
122
|
+
concat text_field_tag scope_name, '', class: "#{classes} filter-rangepicker", data: {behaviour: "filter", scope: scope_name, type: "range-filter"}, placeholder: "#{placeholder}"
|
123
|
+
concat tag.span class: 'fa fa-search search-icon'
|
124
|
+
end
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def clear_filters(clear_path, classes=nil, data_behaviours=nil, clear_text=nil)
|
129
|
+
link_to "#{clear_text}", clear_path, class: "#{classes}", data: data_behaviours
|
130
|
+
end
|
131
|
+
|
86
132
|
def checkbox_row_v2(klass)
|
87
133
|
tag.div class: "row" do
|
88
134
|
SpotlightSearch::Utils.serialize_csv_columns(*klass.enabled_columns).each do |column_path|
|
@@ -94,30 +140,30 @@ module SpotlightSearch
|
|
94
140
|
def create_checkbox_v2(column_path)
|
95
141
|
tag.div class: "col-md-4" do
|
96
142
|
concat check_box_tag "columns[]", column_path, id: column_path.to_s.gsub('/', '-')
|
97
|
-
concat column_path.to_s.gsub('/', '_').humanize
|
143
|
+
concat " " + column_path.to_s.gsub('/', '_').humanize
|
98
144
|
end
|
99
145
|
end
|
100
146
|
|
101
147
|
def cm_paginate(facets)
|
102
|
-
tag.div class: '
|
148
|
+
tag.div class: 'cm-pagination' do
|
103
149
|
tag.div class: 'nav navbar navbar-inner' do
|
104
150
|
tag.ul class: 'pagination' do
|
105
151
|
if facets.previous_page != false
|
106
152
|
previous_page = tag.li do
|
107
|
-
tag.button class: '
|
153
|
+
tag.button class: 'cm-pagination__item', data: { behaviour: 'previous-page'} do
|
108
154
|
tag.span "Previous"
|
109
155
|
end
|
110
156
|
end
|
111
157
|
end
|
112
158
|
current_page = content_tag :li do
|
113
|
-
tag.
|
159
|
+
tag.button class: 'cm-pagination__item', data: {sort_column: facets.sort[:sort_column], sort_direction: facets.sort[:sort_direction], page: facets.current_page, behaviour: 'current-page' } do
|
114
160
|
"Showing #{facets.current_page} of #{facets.total_pages} pages"
|
115
161
|
end
|
116
162
|
end
|
117
163
|
|
118
164
|
if facets.next_page != false
|
119
165
|
next_page = tag.li do
|
120
|
-
tag.button class: '
|
166
|
+
tag.button class: 'cm-pagination__item', data: { behaviour: 'next-page'} do
|
121
167
|
tag.span "Next"
|
122
168
|
end
|
123
169
|
end
|
data/spotlight_search.gemspec
CHANGED
@@ -24,9 +24,8 @@ Gem::Specification.new do |s|
|
|
24
24
|
# s.require_paths = ["lib"]
|
25
25
|
|
26
26
|
s.add_development_dependency "bundler", "~> 2.0"
|
27
|
-
s.add_development_dependency "rake", "
|
27
|
+
s.add_development_dependency "rake", "~> 13.0.1"
|
28
28
|
s.add_development_dependency "rails", "~> 5.2.4.2"
|
29
29
|
s.add_development_dependency "rspec", "~> 3.0"
|
30
|
-
s.add_runtime_dependency '
|
31
|
-
s.add_runtime_dependency 'zip-zip'
|
30
|
+
s.add_runtime_dependency 'caxlsx', "~> 3.0.1"
|
32
31
|
end
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: spotlight_search
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.2
|
4
|
+
version: 0.3.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Anbazhagan Palani
|
@@ -28,16 +28,16 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - "~>"
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version:
|
33
|
+
version: 13.0.1
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - "~>"
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version:
|
40
|
+
version: 13.0.1
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
42
|
name: rails
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
@@ -67,33 +67,19 @@ dependencies:
|
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '3.0'
|
69
69
|
- !ruby/object:Gem::Dependency
|
70
|
-
name:
|
70
|
+
name: caxlsx
|
71
71
|
requirement: !ruby/object:Gem::Requirement
|
72
72
|
requirements:
|
73
|
-
- - "
|
74
|
-
- !ruby/object:Gem::Version
|
75
|
-
version: '0'
|
76
|
-
type: :runtime
|
77
|
-
prerelease: false
|
78
|
-
version_requirements: !ruby/object:Gem::Requirement
|
79
|
-
requirements:
|
80
|
-
- - ">="
|
81
|
-
- !ruby/object:Gem::Version
|
82
|
-
version: '0'
|
83
|
-
- !ruby/object:Gem::Dependency
|
84
|
-
name: zip-zip
|
85
|
-
requirement: !ruby/object:Gem::Requirement
|
86
|
-
requirements:
|
87
|
-
- - ">="
|
73
|
+
- - "~>"
|
88
74
|
- !ruby/object:Gem::Version
|
89
|
-
version:
|
75
|
+
version: 3.0.1
|
90
76
|
type: :runtime
|
91
77
|
prerelease: false
|
92
78
|
version_requirements: !ruby/object:Gem::Requirement
|
93
79
|
requirements:
|
94
|
-
- - "
|
80
|
+
- - "~>"
|
95
81
|
- !ruby/object:Gem::Version
|
96
|
-
version:
|
82
|
+
version: 3.0.1
|
97
83
|
description: |-
|
98
84
|
This gem should help reduce the efforts in the admin panel.
|
99
85
|
It has search, sort and pagination included
|
@@ -120,6 +106,18 @@ files:
|
|
120
106
|
- bin/console
|
121
107
|
- bin/setup
|
122
108
|
- config/routes.rb
|
109
|
+
- lib/generators/spotlight_search/filter_generator.rb
|
110
|
+
- lib/generators/spotlight_search/install_generator.rb
|
111
|
+
- lib/generators/spotlight_search/templates/application.css.scss
|
112
|
+
- lib/generators/spotlight_search/templates/application.js
|
113
|
+
- lib/generators/spotlight_search/templates/coffee.js
|
114
|
+
- lib/generators/spotlight_search/templates/controller.rb.erb
|
115
|
+
- lib/generators/spotlight_search/templates/environment.js
|
116
|
+
- lib/generators/spotlight_search/templates/filters.html.erb
|
117
|
+
- lib/generators/spotlight_search/templates/scaffolds.coffee
|
118
|
+
- lib/generators/spotlight_search/templates/spotlight_search.rb
|
119
|
+
- lib/generators/spotlight_search/templates/webpacker.yml
|
120
|
+
- lib/generators/spotlight_search/templates/webpacker_gem_assets.rb
|
123
121
|
- lib/spotlight_search.rb
|
124
122
|
- lib/spotlight_search/engine.rb
|
125
123
|
- lib/spotlight_search/exceptions.rb
|