refine-rails 2.9.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 +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
|
+
}
|