refine-rails 2.9.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/README.md +413 -0
- data/Rakefile +8 -0
- data/app/assets/config/refine_rails_manifest.js +0 -0
- data/app/assets/javascripts/refine-stimulus.esm.js +2 -0
- data/app/assets/javascripts/refine-stimulus.esm.js.map +1 -0
- data/app/assets/javascripts/refine-stimulus.js +2 -0
- data/app/assets/javascripts/refine-stimulus.js.map +1 -0
- data/app/assets/javascripts/refine-stimulus.modern.js +2 -0
- data/app/assets/javascripts/refine-stimulus.modern.js.map +1 -0
- data/app/assets/javascripts/refine-stimulus.umd.js +2 -0
- data/app/assets/javascripts/refine-stimulus.umd.js.map +1 -0
- data/app/assets/stylesheets/index.css +1873 -0
- data/app/assets/stylesheets/index.tailwind.css +1035 -0
- data/app/controllers/refine/blueprints_controller.rb +80 -0
- data/app/controllers/refine/filter_application_controller.rb +29 -0
- data/app/controllers/refine/inline/criteria_controller.rb +161 -0
- data/app/controllers/refine/inline/stored_filters_controller.rb +84 -0
- data/app/controllers/refine/stored_filters_controller.rb +69 -0
- data/app/javascript/controllers/index.js +66 -0
- data/app/javascript/controllers/refine/add-controller.js +42 -0
- data/app/javascript/controllers/refine/criterion-form-controller.js +31 -0
- data/app/javascript/controllers/refine/date-controller.js +113 -0
- data/app/javascript/controllers/refine/defaults-controller.js +32 -0
- data/app/javascript/controllers/refine/delete-controller.js +13 -0
- data/app/javascript/controllers/refine/filter-pills-controller.js +63 -0
- data/app/javascript/controllers/refine/form-controller.js +51 -0
- data/app/javascript/controllers/refine/inline-conditions-controller.js +33 -0
- data/app/javascript/controllers/refine/popup-controller.js +46 -0
- data/app/javascript/controllers/refine/search-filter-controller.js +50 -0
- data/app/javascript/controllers/refine/server-refresh-controller.js +43 -0
- data/app/javascript/controllers/refine/state-controller.js +220 -0
- data/app/javascript/controllers/refine/stored-filter-controller.js +23 -0
- data/app/javascript/controllers/refine/submit-form-controller.js +8 -0
- data/app/javascript/controllers/refine/toggle-controller.js +12 -0
- data/app/javascript/controllers/refine/turbo-stream-form-controller.js +24 -0
- data/app/javascript/controllers/refine/turbo-stream-link-controller.js +24 -0
- data/app/javascript/controllers/refine/update-controller.js +86 -0
- data/app/javascript/index.js +1 -0
- data/app/javascript/refine/helpers/index.js +77 -0
- data/app/models/refine/blueprints/blueprint.rb +58 -0
- data/app/models/refine/blueprints/blueprint_example.json +25 -0
- data/app/models/refine/conditions/boolean_condition.rb +112 -0
- data/app/models/refine/conditions/clause.rb +38 -0
- data/app/models/refine/conditions/clauses.rb +38 -0
- data/app/models/refine/conditions/condition.rb +285 -0
- data/app/models/refine/conditions/condition_error.rb +1 -0
- data/app/models/refine/conditions/date_condition.rb +464 -0
- data/app/models/refine/conditions/date_with_time_condition.rb +8 -0
- data/app/models/refine/conditions/errors/condition_clause_error.rb +7 -0
- data/app/models/refine/conditions/errors/criteria_limit_exceeded_error.rb +2 -0
- data/app/models/refine/conditions/errors/option_error.rb +2 -0
- data/app/models/refine/conditions/errors/relationship_error.rb +1 -0
- data/app/models/refine/conditions/filter_condition.rb +93 -0
- data/app/models/refine/conditions/has_clauses.rb +117 -0
- data/app/models/refine/conditions/has_meta.rb +10 -0
- data/app/models/refine/conditions/has_refinements.rb +156 -0
- data/app/models/refine/conditions/numeric_condition.rb +224 -0
- data/app/models/refine/conditions/option_condition.rb +260 -0
- data/app/models/refine/conditions/text_condition.rb +152 -0
- data/app/models/refine/conditions/uses_attributes.rb +168 -0
- data/app/models/refine/filter.rb +302 -0
- data/app/models/refine/filters/blueprint_editor.rb +102 -0
- data/app/models/refine/filters/builder.rb +59 -0
- data/app/models/refine/filters/criterion.rb +87 -0
- data/app/models/refine/filters/query.rb +82 -0
- data/app/models/refine/inline/criteria/input.rb +50 -0
- data/app/models/refine/inline/criteria/numeric_refinement.rb +13 -0
- data/app/models/refine/inline/criteria/option.rb +2 -0
- data/app/models/refine/inline/criterion.rb +141 -0
- data/app/models/refine/invalid_filter_error.rb +8 -0
- data/app/models/refine/stabilize.rb +29 -0
- data/app/models/refine/stabilizers/database_stabilizer.rb +21 -0
- data/app/models/refine/stabilizers/errors/url_stabilizer_error.rb +2 -0
- data/app/models/refine/stabilizers/url_encoded_stabilizer.rb +21 -0
- data/app/models/refine/stored_filter.rb +14 -0
- data/app/models/refine/tracks_pending_relationship_subqueries.rb +196 -0
- data/app/views/_filter_builder_dropdown.html.erb +63 -0
- data/app/views/_filter_pills.html.erb +40 -0
- data/app/views/_loading.html.erb +32 -0
- data/app/views/refine/blueprints/_add_and.html.erb +25 -0
- data/app/views/refine/blueprints/_add_group.html.erb +24 -0
- data/app/views/refine/blueprints/_clause_select.html.erb +24 -0
- data/app/views/refine/blueprints/_condition_select.html.erb +53 -0
- data/app/views/refine/blueprints/_criterion.html.erb +41 -0
- data/app/views/refine/blueprints/_criterion_errors.html.erb +7 -0
- data/app/views/refine/blueprints/_delete_criterion.html.erb +11 -0
- data/app/views/refine/blueprints/_group.html.erb +13 -0
- data/app/views/refine/blueprints/_query.html.erb +34 -0
- data/app/views/refine/blueprints/_stored_filters.html.erb +23 -0
- data/app/views/refine/blueprints/clauses/_date_condition.html.erb +80 -0
- data/app/views/refine/blueprints/clauses/_date_picker.html.erb +26 -0
- data/app/views/refine/blueprints/clauses/_filter_condition.html.erb +36 -0
- data/app/views/refine/blueprints/clauses/_numeric_condition.html.erb +35 -0
- data/app/views/refine/blueprints/clauses/_option_condition.html.erb +37 -0
- data/app/views/refine/blueprints/clauses/_text_condition.html.erb +13 -0
- data/app/views/refine/blueprints/create.turbo_stream.erb +22 -0
- data/app/views/refine/blueprints/new.html.erb +7 -0
- data/app/views/refine/blueprints/show.html.erb +4 -0
- data/app/views/refine/blueprints/show.turbo_stream.erb +22 -0
- data/app/views/refine/inline/criteria/_form_fields.html.erb +62 -0
- data/app/views/refine/inline/criteria/create.turbo_stream.erb +19 -0
- data/app/views/refine/inline/criteria/edit.turbo_stream.erb +26 -0
- data/app/views/refine/inline/criteria/index.html.erb +64 -0
- data/app/views/refine/inline/criteria/new.turbo_stream.erb +24 -0
- data/app/views/refine/inline/filters/_add_first_condition_button.html.erb +19 -0
- data/app/views/refine/inline/filters/_and_button.html.erb +26 -0
- data/app/views/refine/inline/filters/_criterion.html.erb +23 -0
- data/app/views/refine/inline/filters/_group.html.erb +13 -0
- data/app/views/refine/inline/filters/_load_button.html.erb +15 -0
- data/app/views/refine/inline/filters/_or_button.html.erb +26 -0
- data/app/views/refine/inline/filters/_popup.html.erb +26 -0
- data/app/views/refine/inline/filters/_save_button.html.erb +15 -0
- data/app/views/refine/inline/filters/_show.html.erb +40 -0
- data/app/views/refine/inline/inputs/_date_condition.html.erb +7 -0
- data/app/views/refine/inline/inputs/_date_condition_days.html.erb +18 -0
- data/app/views/refine/inline/inputs/_date_condition_range.html.erb +22 -0
- data/app/views/refine/inline/inputs/_date_condition_single.html.erb +9 -0
- data/app/views/refine/inline/inputs/_date_picker.html.erb +20 -0
- data/app/views/refine/inline/inputs/_numeric_condition.html.erb +23 -0
- data/app/views/refine/inline/inputs/_option_condition.html.erb +14 -0
- data/app/views/refine/inline/inputs/_text_condition.html.erb +8 -0
- data/app/views/refine/inline/stored_filters/find.turbo_stream.erb +19 -0
- data/app/views/refine/inline/stored_filters/index.html.erb +28 -0
- data/app/views/refine/inline/stored_filters/new.turbo_stream.erb +47 -0
- data/app/views/refine/stored_filters/create.turbo_stream.erb +2 -0
- data/app/views/refine/stored_filters/find.turbo_stream.erb +5 -0
- data/app/views/refine/stored_filters/index.html.erb +39 -0
- data/app/views/refine/stored_filters/new.html.erb +29 -0
- data/app/views/refine/stored_filters/show.html.erb +1 -0
- data/config/locales/en/dates.en.yml +29 -0
- data/config/locales/en/en.yml +20 -0
- data/config/locales/en/refine.en.yml +187 -0
- data/config/routes.rb +17 -0
- data/lib/generators/filter/filter_generator.rb +27 -0
- data/lib/generators/filter/templates/filter.rb.erb +20 -0
- data/lib/refine/rails/engine.rb +15 -0
- data/lib/refine/rails/version.rb +5 -0
- data/lib/refine/rails.rb +38 -0
- data/lib/tasks/refine/rails_tasks.rake +13 -0
- metadata +202 -0
@@ -0,0 +1,80 @@
|
|
1
|
+
module Refine
|
2
|
+
class BlueprintsController < ApplicationController
|
3
|
+
layout false
|
4
|
+
before_action :set_builder
|
5
|
+
before_action :set_filter
|
6
|
+
before_action :set_form
|
7
|
+
|
8
|
+
# entry point for initial render of the filter builder
|
9
|
+
def new
|
10
|
+
@show_stored_filters = params[:stored_filters]
|
11
|
+
end
|
12
|
+
|
13
|
+
# refresh the filter builder
|
14
|
+
def show
|
15
|
+
if @refine_filter_query.valid?
|
16
|
+
@stable_id = @refine_filter.to_stable_id
|
17
|
+
end
|
18
|
+
|
19
|
+
# don't display errors
|
20
|
+
include_errors = ActiveRecord::Type::Boolean.new.cast(params[:include_errors])
|
21
|
+
@refine_filter_query.clear_errors unless include_errors
|
22
|
+
|
23
|
+
respond_to do |format|
|
24
|
+
format.turbo_stream
|
25
|
+
format.html
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# handles filter submission
|
30
|
+
def create
|
31
|
+
if @refine_filter_query.valid?
|
32
|
+
# set stable_id
|
33
|
+
@stable_id = @refine_filter.to_stable_id
|
34
|
+
|
35
|
+
# set url
|
36
|
+
uri = URI(request.referrer)
|
37
|
+
new_query_ar = URI.decode_www_form(String(uri.query))
|
38
|
+
new_query_ar.reject! { |(k, _v)| k == "stable_id" }
|
39
|
+
new_query_ar << ["stable_id", @stable_id]
|
40
|
+
uri.query = URI.encode_www_form(new_query_ar)
|
41
|
+
@url_for_redirect = uri.to_s
|
42
|
+
@filter_submit_success = true
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
46
|
+
def stored_filters
|
47
|
+
render partial: "stored_filters", layout: false
|
48
|
+
end
|
49
|
+
|
50
|
+
def validate
|
51
|
+
if @refine_filter_query.valid?
|
52
|
+
head :ok
|
53
|
+
else
|
54
|
+
head :unprocessable_entity
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
private
|
59
|
+
|
60
|
+
def set_builder
|
61
|
+
builder_params = params.require(:refine_filters_builder).permit(
|
62
|
+
:blueprint_json,
|
63
|
+
:filter_class,
|
64
|
+
:stable_id,
|
65
|
+
:stored_filter_id,
|
66
|
+
:client_id
|
67
|
+
)
|
68
|
+
|
69
|
+
@refine_filter_builder = Refine::Filters::Builder.new(builder_params)
|
70
|
+
end
|
71
|
+
|
72
|
+
def set_filter
|
73
|
+
@refine_filter = @refine_filter_builder.refine_filter
|
74
|
+
end
|
75
|
+
|
76
|
+
def set_form
|
77
|
+
@refine_filter_query = @refine_filter_builder.query
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
module Refine::FilterApplicationController
|
2
|
+
# Optional module to get started quickly.You can send in the current controller's instance variable if you'd like to update the collection here
|
3
|
+
# The current scope can be used to modify the query
|
4
|
+
|
5
|
+
|
6
|
+
def apply_filter(filter_class, initial_query: nil)
|
7
|
+
if filter_class.present?
|
8
|
+
@stable_id = params[:stable_id]
|
9
|
+
@refine_filter_builder = Refine::Filters::Builder.new(
|
10
|
+
stable_id: @stable_id,
|
11
|
+
filter_class: filter_class.name,
|
12
|
+
initial_query: initial_query)
|
13
|
+
@refine_filter = @refine_filter_builder.refine_filter
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
# Use this on pages that use the new inline filter
|
18
|
+
def apply_inline_filter(filter_class, initial_query: nil)
|
19
|
+
stable_id = params[:stable_id]
|
20
|
+
|
21
|
+
if stable_id.present?
|
22
|
+
@refine_filter = Refine::Rails.configuration.stabilizer_classes[:url].new.from_stable_id(id: stable_id, initial_query: initial_query)
|
23
|
+
else
|
24
|
+
@refine_filter = filter_class.new([], initial_query)
|
25
|
+
end
|
26
|
+
@refine_client_id = SecureRandom.uuid
|
27
|
+
@refine_stable_id = @refine_filter.to_stable_id
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,161 @@
|
|
1
|
+
class Refine::Inline::CriteriaController < ApplicationController
|
2
|
+
layout false
|
3
|
+
before_action :set_refine_filter
|
4
|
+
|
5
|
+
# List available conditions for new criteria
|
6
|
+
# Carries position and index forward
|
7
|
+
def index
|
8
|
+
@criterion = Refine::Inline::Criterion.new(criterion_params.merge(refine_filter: @refine_filter))
|
9
|
+
@conditions = @refine_filter.instantiated_conditions
|
10
|
+
end
|
11
|
+
|
12
|
+
# Show the form to add a new criteria
|
13
|
+
def new
|
14
|
+
@criterion = Refine::Inline::Criterion.new(criterion_params.merge(refine_filter: @refine_filter))
|
15
|
+
respond_to do |format|
|
16
|
+
# render a turbo_stream so that we can render inputs and forms in separate areas of the page
|
17
|
+
# in case we're nested inside a form
|
18
|
+
format.turbo_stream
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
# Create a new criterion
|
23
|
+
def create
|
24
|
+
@criterion = Refine::Inline::Criterion.new(criterion_params.merge(refine_filter: @refine_filter))
|
25
|
+
blueprint = @refine_filter.blueprint
|
26
|
+
|
27
|
+
Refine::Filters::BlueprintEditor
|
28
|
+
.new(@refine_filter.blueprint)
|
29
|
+
.add(
|
30
|
+
position: @criterion.position.to_i,
|
31
|
+
conjunction: @criterion.conjunction,
|
32
|
+
criterion: @criterion.to_blueprint_node
|
33
|
+
)
|
34
|
+
|
35
|
+
@error_messages = filter_error_messages(@refine_filter)
|
36
|
+
if @error_messages.none?
|
37
|
+
handle_filter_update(@refine_filter.to_stable_id)
|
38
|
+
else
|
39
|
+
respond_to do |format|
|
40
|
+
format.turbo_stream { render :new }
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
# show the form to edit an existing criterion
|
46
|
+
def edit
|
47
|
+
@criterion = Refine::Inline::Criterion
|
48
|
+
.groups_from_filter(@refine_filter, **criterion_params.slice(:client_id, :stable_id))
|
49
|
+
.flatten
|
50
|
+
.detect { |c| c.position.to_s == params[:id] }
|
51
|
+
|
52
|
+
@criterion.attributes = criterion_params
|
53
|
+
respond_to do |format|
|
54
|
+
# render a turbo_stream so that we can render inputs and forms in separate areas of the page
|
55
|
+
# in case we're nested inside a form
|
56
|
+
format.turbo_stream
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
# update an exsiting criterion
|
61
|
+
def update
|
62
|
+
@criterion = Refine::Inline::Criterion.new(criterion_params.merge(refine_filter: @refine_filter))
|
63
|
+
Refine::Filters::BlueprintEditor
|
64
|
+
.new(@refine_filter.blueprint)
|
65
|
+
.update(params[:id].to_i, criterion: @criterion.to_blueprint_node)
|
66
|
+
|
67
|
+
@error_messages = filter_error_messages(@refine_filter)
|
68
|
+
if @error_messages.none?
|
69
|
+
handle_filter_update(@refine_filter.to_stable_id)
|
70
|
+
else
|
71
|
+
respond_to do |format|
|
72
|
+
format.turbo_stream { render :edit }
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
|
77
|
+
# remove an existing criterion
|
78
|
+
def destroy
|
79
|
+
@criterion = Refine::Inline::Criterion
|
80
|
+
.groups_from_filter(@refine_filter, **criterion_params.slice(:client_id, :stable_id))
|
81
|
+
.flatten
|
82
|
+
.detect { |c| c.position.to_s == params[:id] }
|
83
|
+
|
84
|
+
Refine::Filters::BlueprintEditor
|
85
|
+
.new(@refine_filter.blueprint)
|
86
|
+
.delete(params[:id].to_i)
|
87
|
+
|
88
|
+
handle_filter_update(@refine_filter.to_stable_id)
|
89
|
+
end
|
90
|
+
|
91
|
+
private
|
92
|
+
|
93
|
+
def set_refine_filter
|
94
|
+
@refine_filter ||= Refine::Rails.configuration.stabilizer_classes[:url]
|
95
|
+
.new
|
96
|
+
.from_stable_id(id: criterion_params[:stable_id])
|
97
|
+
end
|
98
|
+
|
99
|
+
def criterion_params
|
100
|
+
params.require(:refine_inline_criterion).permit(
|
101
|
+
:stable_id,
|
102
|
+
:client_id,
|
103
|
+
:condition_id,
|
104
|
+
:position,
|
105
|
+
:conjunction,
|
106
|
+
input_attributes: [
|
107
|
+
:clause,
|
108
|
+
:date1,
|
109
|
+
:date2,
|
110
|
+
:days,
|
111
|
+
:modifier,
|
112
|
+
:selected,
|
113
|
+
:value,
|
114
|
+
:value1,
|
115
|
+
:value2,
|
116
|
+
selected: [],
|
117
|
+
count_refinement_attributes: [
|
118
|
+
:clause,
|
119
|
+
:value1,
|
120
|
+
:value2
|
121
|
+
]
|
122
|
+
]
|
123
|
+
)
|
124
|
+
end
|
125
|
+
|
126
|
+
# either directly redirect or emit a `filter-submit-success` event
|
127
|
+
def handle_filter_update stable_id
|
128
|
+
# update_stable_id in url
|
129
|
+
uri = URI(request.referrer)
|
130
|
+
new_query_ar = URI.decode_www_form(String(uri.query))
|
131
|
+
new_query_ar.reject! { |(k, _v)| k == "stable_id" }
|
132
|
+
new_query_ar << ["stable_id", stable_id]
|
133
|
+
uri.query = URI.encode_www_form(new_query_ar)
|
134
|
+
|
135
|
+
respond_to do |format|
|
136
|
+
format.turbo_stream do
|
137
|
+
@refine_stable_id = stable_id
|
138
|
+
@url_for_redirect = uri
|
139
|
+
@refine_client_id = @criterion.client_id
|
140
|
+
render :create
|
141
|
+
end
|
142
|
+
format.html {redirect_to uri.to_s }
|
143
|
+
end
|
144
|
+
end
|
145
|
+
|
146
|
+
def filter_valid?(refine_filter)
|
147
|
+
Refine::Inline::Criterion
|
148
|
+
.groups_from_filter(refine_filter, **criterion_params.slice(:client_id, :stable_id))
|
149
|
+
.flatten
|
150
|
+
.all?(&:valid?)
|
151
|
+
end
|
152
|
+
|
153
|
+
def filter_error_messages(refine_filter)
|
154
|
+
criteria = Refine::Inline::Criterion
|
155
|
+
.groups_from_filter(refine_filter, **criterion_params.slice(:client_id, :stable_id))
|
156
|
+
.flatten
|
157
|
+
|
158
|
+
criteria.each(&:validate!)
|
159
|
+
criteria.flat_map {|c| c.errors.full_messages }
|
160
|
+
end
|
161
|
+
end
|
@@ -0,0 +1,84 @@
|
|
1
|
+
class Refine::Inline::StoredFiltersController < ApplicationController
|
2
|
+
layout false
|
3
|
+
before_action :set_refine_filter
|
4
|
+
|
5
|
+
|
6
|
+
def index
|
7
|
+
@criterion = Refine::Inline::Criterion.new(criterion_params)
|
8
|
+
@stored_filters = Refine::StoredFilter.where(filter_type: @refine_filter.type)
|
9
|
+
@stored_filters = instance_exec(@stored_filters, &Refine::Rails.configuration.stored_filter_scope)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find
|
13
|
+
@stored_filter = Refine::StoredFilter.find_by(id: params[:id], filter_type: @refine_filter.type)
|
14
|
+
handle_filter_update @stored_filter.refine_filter.to_stable_id
|
15
|
+
end
|
16
|
+
|
17
|
+
def new
|
18
|
+
@criterion = Refine::Inline::Criterion.new(criterion_params)
|
19
|
+
@stored_filter = Refine::StoredFilter.new(filter_type: @refine_filter.type)
|
20
|
+
respond_to do |format|
|
21
|
+
format.turbo_stream
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
def create
|
26
|
+
@criterion = Refine::Inline::Criterion.new(criterion_params)
|
27
|
+
@stored_filter = Refine::StoredFilter.new(stored_filter_params.merge(
|
28
|
+
state: @refine_filter.state,
|
29
|
+
filter_type: @refine_filter.type,
|
30
|
+
**instance_exec(&Refine::Rails.configuration.custom_stored_filter_attributes)
|
31
|
+
)
|
32
|
+
)
|
33
|
+
|
34
|
+
if @stored_filter.save
|
35
|
+
handle_filter_update @stored_filter.refine_filter.to_stable_id
|
36
|
+
else
|
37
|
+
respond_to do |format|
|
38
|
+
format.turbo_stream { render :new }
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def set_refine_filter
|
46
|
+
@refine_filter ||= Refine::Rails.configuration.stabilizer_classes[:url]
|
47
|
+
.new
|
48
|
+
.from_stable_id(id: criterion_params[:stable_id])
|
49
|
+
end
|
50
|
+
|
51
|
+
def criterion_params
|
52
|
+
params.require(:refine_inline_criterion).permit(
|
53
|
+
:stable_id,
|
54
|
+
:client_id,
|
55
|
+
:position,
|
56
|
+
:conjunction
|
57
|
+
)
|
58
|
+
end
|
59
|
+
|
60
|
+
def stored_filter_params
|
61
|
+
params.require(:refine_stored_filter).permit(:name)
|
62
|
+
end
|
63
|
+
|
64
|
+
# either directly redirect or emit a `filter-submit-success` event
|
65
|
+
def handle_filter_update stable_id
|
66
|
+
# update_stable_id in url
|
67
|
+
uri = URI(request.referrer)
|
68
|
+
new_query_ar = URI.decode_www_form(String(uri.query))
|
69
|
+
new_query_ar.reject! { |(k, _v)| k == "stable_id" }
|
70
|
+
new_query_ar << ["stable_id", stable_id]
|
71
|
+
uri.query = URI.encode_www_form(new_query_ar)
|
72
|
+
|
73
|
+
respond_to do |format|
|
74
|
+
format.turbo_stream do
|
75
|
+
@refine_stable_id = stable_id
|
76
|
+
@url_for_redirect = uri
|
77
|
+
@refine_client_id = criterion_params[:client_id]
|
78
|
+
@refine_filter = @stored_filter.refine_filter
|
79
|
+
render :find
|
80
|
+
end
|
81
|
+
format.html {redirect_to uri.to_s }
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
@@ -0,0 +1,69 @@
|
|
1
|
+
module Refine
|
2
|
+
class StoredFiltersController < ApplicationController
|
3
|
+
layout false
|
4
|
+
|
5
|
+
before_action :set_builder
|
6
|
+
|
7
|
+
def index
|
8
|
+
@stored_filters = StoredFilter.where(filter_type: @refine_filter_builder.filter_class)
|
9
|
+
@stored_filters = instance_exec(@stored_filters, &Refine::Rails.configuration.stored_filter_scope)
|
10
|
+
end
|
11
|
+
|
12
|
+
def find
|
13
|
+
@stored_filter = StoredFilter.find_by(id: params[:id])
|
14
|
+
@refine_filter_builder.stored_filter_id = @stored_filter.id
|
15
|
+
|
16
|
+
# KLUDGE we need a base scope for determining if the filter is valid.
|
17
|
+
# We set initial_queryto model.all because this filter is used just for validation
|
18
|
+
# and is never evaluated.
|
19
|
+
#
|
20
|
+
# This method is a temporary workaround until we clean up input validation and
|
21
|
+
# filter evaluation
|
22
|
+
filter_for_model = @stored_filter.refine_filter
|
23
|
+
model = filter_for_model.model # AR::Base subclass
|
24
|
+
@refine_filter = @stored_filter.refine_filter(initial_query: model.all)
|
25
|
+
@refine_filter_query = Refine::Filters::Query.new(@refine_filter)
|
26
|
+
unless @refine_filter.valid_query?
|
27
|
+
redirect_to refine_stored_filters_path(@refine_filter_builder.to_params),
|
28
|
+
alert: "Sorry, that filter is not valid"
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def new
|
33
|
+
@stored_filter = StoredFilter.new(filter_type: @refine_filter_builder.filter_class)
|
34
|
+
end
|
35
|
+
|
36
|
+
def create
|
37
|
+
@refine_filter = @refine_filter_builder.refine_filter
|
38
|
+
|
39
|
+
@stored_filter = StoredFilter.new(name: params[:name], state: @refine_filter.state, filter_type: @refine_filter.type, **instance_exec(&Refine::Rails.configuration.custom_stored_filter_attributes))
|
40
|
+
@refine_filter_query = @refine_filter_builder.query
|
41
|
+
|
42
|
+
if !@refine_filter_query.valid?
|
43
|
+
# replace the filter form with errors
|
44
|
+
render :create
|
45
|
+
elsif !@stored_filter.save
|
46
|
+
# replace the stored filter form
|
47
|
+
render :new, status: :unprocessable_entity
|
48
|
+
else
|
49
|
+
# return to stored filters header and load the filter into the query builder
|
50
|
+
@refine_filter_builder.stored_filter_id = @stored_filter.id
|
51
|
+
render :find
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def set_builder
|
58
|
+
builder_params = params.require(:refine_filters_builder).permit(
|
59
|
+
:blueprint_json,
|
60
|
+
:filter_class,
|
61
|
+
:stable_id,
|
62
|
+
:stored_filter_id,
|
63
|
+
:client_id,
|
64
|
+
)
|
65
|
+
|
66
|
+
@refine_filter_builder = Refine::Filters::Builder.new(builder_params)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
end
|
@@ -0,0 +1,66 @@
|
|
1
|
+
import { identifierForContextKey } from "@hotwired/stimulus-webpack-helpers"
|
2
|
+
|
3
|
+
import AddController from './refine/add-controller'
|
4
|
+
import InlineConditionsController from './refine/inline-conditions-controller'
|
5
|
+
import CriterionFormController from './refine/criterion-form-controller'
|
6
|
+
import DefaultsController from './refine/defaults-controller'
|
7
|
+
import DeleteController from './refine/delete-controller'
|
8
|
+
import FilterPillsController from './refine/filter-pills-controller'
|
9
|
+
import PopupController from './refine/popup-controller'
|
10
|
+
import SearchFilterController from './refine/search-filter-controller'
|
11
|
+
import ServerRefreshController from './refine/server-refresh-controller'
|
12
|
+
import StateController from './refine/state-controller'
|
13
|
+
import StoredFilterController from './refine/stored-filter-controller'
|
14
|
+
import SubmitForm from './refine/submit-form-controller'
|
15
|
+
import ToggleController from './refine/toggle-controller'
|
16
|
+
import TurboStreamFormController from './refine/turbo-stream-form-controller'
|
17
|
+
import TurboStreamLinkController from './refine/turbo-stream-link-controller'
|
18
|
+
import UpdateController from './refine/update-controller'
|
19
|
+
import DateController from './refine/date-controller'
|
20
|
+
|
21
|
+
export const controllerDefinitions = [
|
22
|
+
[AddController, 'refine/add-controller.js'],
|
23
|
+
[InlineConditionsController, './refine/inline-conditions-controller.js'],
|
24
|
+
[CriterionFormController, 'refine/criterion-form-controller.js'],
|
25
|
+
[DefaultsController, 'refine/defaults-controller.js'],
|
26
|
+
[DeleteController, 'refine/delete-controller.js'],
|
27
|
+
[FilterPillsController, 'refine/filter-pills-controller.js'],
|
28
|
+
[PopupController, 'refine/popup-controller.js'],
|
29
|
+
[SearchFilterController, 'refine/search-filter-controller.js'],
|
30
|
+
[ServerRefreshController, 'refine/server-refresh-controller.js'],
|
31
|
+
[StateController, 'refine/state-controller.js'],
|
32
|
+
[StoredFilterController, 'refine/stored-filter-controller.js'],
|
33
|
+
[SubmitForm, 'refine/submit-form-controller.js'],
|
34
|
+
[ToggleController, 'refine/toggle-controller.js'],
|
35
|
+
[TurboStreamFormController, 'refine/turbo-stream-form-controller.js'],
|
36
|
+
[TurboStreamLinkController, 'refine/turbo-stream-link-controller.js'],
|
37
|
+
[UpdateController, 'refine/update-controller.js'],
|
38
|
+
[DateController, 'refine/date-controller.js']
|
39
|
+
].map(function(d) {
|
40
|
+
const key = d[1]
|
41
|
+
const controller = d[0]
|
42
|
+
return {
|
43
|
+
identifier: identifierForContextKey(key),
|
44
|
+
controllerConstructor: controller
|
45
|
+
}
|
46
|
+
})
|
47
|
+
|
48
|
+
export {
|
49
|
+
AddController,
|
50
|
+
InlineConditionsController,
|
51
|
+
CriterionFormController,
|
52
|
+
DefaultsController,
|
53
|
+
DeleteController,
|
54
|
+
FilterPillsController,
|
55
|
+
PopupController,
|
56
|
+
SearchFilterController,
|
57
|
+
ServerRefreshController,
|
58
|
+
StateController,
|
59
|
+
StoredFilterController,
|
60
|
+
SubmitForm,
|
61
|
+
ToggleController,
|
62
|
+
TurboStreamFormController,
|
63
|
+
TurboStreamLinkController,
|
64
|
+
UpdateController,
|
65
|
+
DateController
|
66
|
+
}
|
@@ -0,0 +1,42 @@
|
|
1
|
+
import ServerRefreshController from './server-refresh-controller'
|
2
|
+
import { FetchRequest } from '@rails/request.js'
|
3
|
+
|
4
|
+
export default class extends ServerRefreshController {
|
5
|
+
static values = {
|
6
|
+
previousCriterionId: Number,
|
7
|
+
}
|
8
|
+
|
9
|
+
async criterion() {
|
10
|
+
const isValid = await this.validateBlueprint()
|
11
|
+
if (isValid) {
|
12
|
+
this.state.addCriterion(this.previousCriterionIdValue)
|
13
|
+
}
|
14
|
+
this.refreshFromServer({includeErrors: !isValid})
|
15
|
+
}
|
16
|
+
|
17
|
+
async group() {
|
18
|
+
const isValid = await this.validateBlueprint()
|
19
|
+
if (isValid) {
|
20
|
+
this.state.addGroup()
|
21
|
+
}
|
22
|
+
this.refreshFromServer({includeErrors: !isValid})
|
23
|
+
}
|
24
|
+
|
25
|
+
async validateBlueprint(blueprint) {
|
26
|
+
const { state } = this
|
27
|
+
|
28
|
+
const request = new FetchRequest(
|
29
|
+
"GET",
|
30
|
+
this.state.validateBlueprintUrlValue,
|
31
|
+
{
|
32
|
+
query: {
|
33
|
+
"refine_filters_builder[filter_class]": this.state.filterName,
|
34
|
+
"refine_filters_builder[blueprint_json]": JSON.stringify(this.state.blueprint),
|
35
|
+
"refine_filters_builder[client_id]": this.state.clientIdValue
|
36
|
+
}
|
37
|
+
}
|
38
|
+
)
|
39
|
+
const response = await request.perform()
|
40
|
+
return response.ok
|
41
|
+
}
|
42
|
+
}
|
@@ -0,0 +1,31 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import { FetchRequest } from '@rails/request.js'
|
3
|
+
|
4
|
+
/*
|
5
|
+
This controller handles criteria forms
|
6
|
+
(refine/inline/criteria/new|edit)
|
7
|
+
*/
|
8
|
+
export default class extends Controller {
|
9
|
+
static values = {
|
10
|
+
url: String,
|
11
|
+
turboFrame: String,
|
12
|
+
method: { type: String, default: "POST" }
|
13
|
+
}
|
14
|
+
|
15
|
+
refresh(_event) {
|
16
|
+
// update the url with params from the form
|
17
|
+
const formData = new FormData(this.element)
|
18
|
+
const url = new URL(this.urlValue)
|
19
|
+
|
20
|
+
for (const [name, value] of formData.entries()) {
|
21
|
+
console.log(name, value)
|
22
|
+
url.searchParams.set(name, value)
|
23
|
+
}
|
24
|
+
|
25
|
+
// navigate the modal to refresh the form
|
26
|
+
window.Turbo.visit(url.toString(), {frame: this.turboFrameValue})
|
27
|
+
}
|
28
|
+
|
29
|
+
|
30
|
+
|
31
|
+
}
|
@@ -0,0 +1,113 @@
|
|
1
|
+
import { Controller } from "@hotwired/stimulus"
|
2
|
+
import moment from 'moment'
|
3
|
+
require('daterangepicker/daterangepicker.css')
|
4
|
+
|
5
|
+
// requires jQuery, moment, might want to consider a vanilla JS alternative
|
6
|
+
import $ from 'jquery' // ensure jquery is loaded before daterangepicker
|
7
|
+
import 'daterangepicker'
|
8
|
+
|
9
|
+
export default class extends Controller {
|
10
|
+
static targets = [
|
11
|
+
'field',
|
12
|
+
'hiddenField',
|
13
|
+
'clearButton',
|
14
|
+
]
|
15
|
+
|
16
|
+
static values = {
|
17
|
+
includeTime: Boolean,
|
18
|
+
futureOnly: Boolean,
|
19
|
+
drops: String,
|
20
|
+
inline: Boolean,
|
21
|
+
dateFormat: String,
|
22
|
+
timeFormat: String,
|
23
|
+
isAmPm: Boolean,
|
24
|
+
locale: { type: String, default: 'en' },
|
25
|
+
datetimeFormat: { type: String, default: 'MM/DD/YYYY h:mm A' },
|
26
|
+
pickerLocale: { type: Object, default: {} },
|
27
|
+
}
|
28
|
+
|
29
|
+
connect() {
|
30
|
+
this.initPluginInstance()
|
31
|
+
}
|
32
|
+
|
33
|
+
disconnect() {
|
34
|
+
this.teardownPluginInstance()
|
35
|
+
}
|
36
|
+
|
37
|
+
clearDate(event) {
|
38
|
+
// don't submit the form, unless it originated from the cancel/clear button
|
39
|
+
event.preventDefault()
|
40
|
+
|
41
|
+
window.$(this.fieldTarget).val('')
|
42
|
+
|
43
|
+
this.dispatch('value-cleared')
|
44
|
+
}
|
45
|
+
|
46
|
+
applyDateToField(event, picker) {
|
47
|
+
const format = this.includeTimeValue ? this.timeFormatValue : this.dateFormatValue
|
48
|
+
|
49
|
+
const momentVal = picker
|
50
|
+
? moment(picker.startDate.toISOString())
|
51
|
+
: moment(this.fieldTarget.value, 'YYYY-MM-DDTHH:mm').format('YYYY-MM-DDTHH:mm')
|
52
|
+
const displayVal = momentVal.format(format)
|
53
|
+
const dataVal = this.includeTimeValue ? momentVal.toISOString(true) : momentVal.format('YYYY-MM-DD')
|
54
|
+
|
55
|
+
this.fieldTarget.value = displayVal
|
56
|
+
this.hiddenFieldTarget.value = dataVal
|
57
|
+
// bubble up a change event when the input is updated for other listeners
|
58
|
+
window.$(this.fieldTarget).trigger('change', picker)
|
59
|
+
|
60
|
+
// emit native change event
|
61
|
+
this.hiddenFieldTarget.dispatchEvent(new Event('change', { detail: picker, bubbles: true }))
|
62
|
+
}
|
63
|
+
|
64
|
+
initPluginInstance() {
|
65
|
+
const localeValues = this.pickerLocaleValue
|
66
|
+
const isAmPm = this.isAmPmValue
|
67
|
+
localeValues['format'] = this.includeTimeValue ? this.timeFormatValue : this.dateFormatValue
|
68
|
+
|
69
|
+
window.$(this.fieldTarget).daterangepicker({
|
70
|
+
singleDatePicker: true,
|
71
|
+
timePicker: this.includeTimeValue,
|
72
|
+
timePickerIncrement: 5,
|
73
|
+
autoUpdateInput: false,
|
74
|
+
autoApply: true,
|
75
|
+
minDate: this.futureOnlyValue ? new Date() : false,
|
76
|
+
locale: localeValues,
|
77
|
+
parentEl: $(this.element),
|
78
|
+
drops: this.dropsValue ? this.dropsValue : 'down',
|
79
|
+
timePicker24Hour: !isAmPm,
|
80
|
+
})
|
81
|
+
|
82
|
+
window.$(this.fieldTarget).on('apply.daterangepicker', this.applyDateToField.bind(this))
|
83
|
+
window.$(this.fieldTarget).on('cancel.daterangepicker', this.clearDate.bind(this))
|
84
|
+
window.$(this.fieldTarget).on('showCalendar.daterangepicker', this.showCalendar.bind(this))
|
85
|
+
|
86
|
+
this.pluginMainEl = this.fieldTarget
|
87
|
+
this.plugin = $(this.pluginMainEl).data('daterangepicker') // weird
|
88
|
+
|
89
|
+
if (this.inlineValue) {
|
90
|
+
this.element.classList.add('date-input--inline')
|
91
|
+
}
|
92
|
+
|
93
|
+
}
|
94
|
+
|
95
|
+
teardownPluginInstance() {
|
96
|
+
if (this.plugin === undefined) {
|
97
|
+
return
|
98
|
+
}
|
99
|
+
|
100
|
+
$(this.pluginMainEl).off('apply.daterangepicker')
|
101
|
+
$(this.pluginMainEl).off('cancel.daterangepicker')
|
102
|
+
$(this.pluginMainEl).off('showCalendar.daterangepicker')
|
103
|
+
|
104
|
+
// revert to original markup, remove any event listeners
|
105
|
+
this.plugin.remove()
|
106
|
+
|
107
|
+
}
|
108
|
+
|
109
|
+
showCalendar() {
|
110
|
+
this.dispatch('show-calendar')
|
111
|
+
}
|
112
|
+
|
113
|
+
}
|