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,196 @@
|
|
|
1
|
+
module Refine
|
|
2
|
+
module TracksPendingRelationshipSubqueries
|
|
3
|
+
def pending_relationship_subquery_depth
|
|
4
|
+
@pending_relationship_subquery_depth ||= []
|
|
5
|
+
end
|
|
6
|
+
|
|
7
|
+
def pending_relationship_subqueries
|
|
8
|
+
# The Hash.new block will dynamically created multi-level nested keys
|
|
9
|
+
# Note: Will create a value of {} when accessed with `dig`
|
|
10
|
+
@pending_relationship_subqueries ||= Hash.new { |h, k| h[k] = h.dup.clear }
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
# Allow filter refinements to be collapsable in order to slot them in a the appropriate depth
|
|
14
|
+
def allow_pending_relationship_to_collapse
|
|
15
|
+
pending_relationship_subqueries.dig(*get_current_relationship)[:collapsible] = true
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def set_pending_relationship(relation, instance)
|
|
19
|
+
pending_relationship_subquery_depth << relation.to_sym
|
|
20
|
+
# this populates modelA[:children][:modelB]
|
|
21
|
+
pending_relationship_subqueries.dig(*get_current_relationship)[:instance] = instance
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def get_current_relationship
|
|
25
|
+
pending_relationship_subquery_depth.join(".children.").split(".").map(&:to_sym)
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def add_pending_joins_relationship_subquery(subquery:)
|
|
29
|
+
add_pending_relationship_subquery(subquery: subquery, primary_key: JOINS)
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
def add_pending_relationship_subquery(subquery:, primary_key:, secondary_key: nil, inverse_clause: false)
|
|
33
|
+
# Add key, query, and secondary keys at the correct depth
|
|
34
|
+
pending_relationship_subqueries.dig(*get_current_relationship)[:key] = primary_key
|
|
35
|
+
pending_relationship_subqueries.dig(*get_current_relationship)[:query] = subquery
|
|
36
|
+
pending_relationship_subqueries.dig(*get_current_relationship)[:secondary] = secondary_key
|
|
37
|
+
pending_relationship_subqueries.dig(*get_current_relationship)[:inverse_clause] = inverse_clause
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def get_pending_relationship_instance
|
|
41
|
+
get_pending_relationship_item("instance")
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
def get_pending_relationship_item(key)
|
|
45
|
+
# Digging for a particular key will create a {} value, must check presence.
|
|
46
|
+
if pending_relationship_subqueries.dig(*get_current_relationship, key.to_sym).present?
|
|
47
|
+
pending_relationship_subqueries.dig(*get_current_relationship, key.to_sym)
|
|
48
|
+
else
|
|
49
|
+
nil
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def set_pending_relationship_subquery_wrapper(callback)
|
|
54
|
+
pending_relationship_subqueries.dig(*get_current_relationship)[:wrapper] = callback
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def relationship_supports_collapsing(instance)
|
|
58
|
+
if get_current_relationship.present?
|
|
59
|
+
return true if pending_relationship_subqueries.dig(*get_current_relationship)[:collapsible] == true
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
(instance.is_a? ActiveRecord::Reflection::BelongsToReflection) || (instance.is_a? ActiveRecord::Reflection::HasOneReflection)
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
# This method can be challenging to understand. Walk through the values with the `can handle nested relationships` test.
|
|
66
|
+
# The query will eventually return the following SQL:
|
|
67
|
+
# SELECT "btt_phones".* FROM "btt_phones"
|
|
68
|
+
# WHERE ("btt_phones"."btt_user_id" IN
|
|
69
|
+
# (SELECT "btt_users"."id" FROM "btt_users" WHERE "btt_users"."id" IN
|
|
70
|
+
# (SELECT "btt_notes"."btt_user_id" FROM "btt_notes" WHERE ("btt_notes"."body" LIKE \'%foo%\'))))
|
|
71
|
+
|
|
72
|
+
def release_pending_relationship
|
|
73
|
+
# instance is the ActiveRecord::Reflection::HasManyReflection between btt_notes and BttUser
|
|
74
|
+
instance = get_pending_relationship_instance
|
|
75
|
+
# Pop off the last key (last relationship)
|
|
76
|
+
# popped = :btt_notes
|
|
77
|
+
popped = pending_relationship_subquery_depth.pop.to_sym
|
|
78
|
+
return if relationship_supports_collapsing(instance)
|
|
79
|
+
# current = [:btt_user]
|
|
80
|
+
current = get_current_relationship
|
|
81
|
+
if current.blank?
|
|
82
|
+
@immediately_commit_pending_relationship_subqueries = true
|
|
83
|
+
return
|
|
84
|
+
end
|
|
85
|
+
# Grab the query one level higher than the current stack (removed during pop)
|
|
86
|
+
# query is the pending_relationship_subqueries[:btt_user][:query] = "SELECT `btt_users`.`id` FROM `btt_users`"
|
|
87
|
+
query = pending_relationship_subqueries.dig(*current)[:query]
|
|
88
|
+
# Build hash to send to commit_subset with popped -> value at popped
|
|
89
|
+
subset = {}
|
|
90
|
+
# subset is a hash with key [:btt_notes] (the popped relationship) = value at popped.
|
|
91
|
+
# subset[:btt_notes].keys = [:instance, :query, :key, :secondary, :inverse_clause]
|
|
92
|
+
# subset[:btt_notes][:query].to_sql
|
|
93
|
+
# "SELECT `btt_notes`.`btt_user_id` FROM `btt_notes` WHERE (`btt_notes`.`body` LIKE '%foo%')"
|
|
94
|
+
|
|
95
|
+
subset[popped] = pending_relationship_subqueries.dig(*current)[:children][popped]
|
|
96
|
+
# Remove popped from pending relationships subqueries -> handled in commit_subset
|
|
97
|
+
# We are removing the "child" relationship we just set to subset (in this case :btt_notes)
|
|
98
|
+
# pending_relationship_subqueries[:btt_user][:children] = {}
|
|
99
|
+
# And assigning the value at [:btt_user][:query] to what is returned in commit_subset
|
|
100
|
+
pending_relationship_subqueries.dig(*current)[:children].except!(popped)
|
|
101
|
+
# query is the high level linking query = "SELECT `btt_users`.`id` FROM `btt_users`"
|
|
102
|
+
# and subset is the hash of the child relationship we just removed from pending_relationship_subqueries
|
|
103
|
+
# subset[:btt_notes].keys = [:instance, :query, :key, :secondary, :inverse_clause]
|
|
104
|
+
pending_relationship_subqueries.dig(*current)[:query] = commit_subset(subset: subset, query: query)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def commit_pending_relationship_subqueries
|
|
108
|
+
applied_query = commit_subset(subset: pending_relationship_subqueries)
|
|
109
|
+
@pending_relationship_subqueries = Hash.new { |h, k| h[k] = h.dup.clear }
|
|
110
|
+
applied_query
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
def commit_subset(subset:, query: nil)
|
|
114
|
+
# Turn pending relationship subqueries into nodes to apply
|
|
115
|
+
# Subquery below is the hash value which has a query key.
|
|
116
|
+
subset.each do |relation, subquery|
|
|
117
|
+
# relation = table
|
|
118
|
+
# subquery.keys = [:instance, :query, :key, :secondary] possibly (:children)
|
|
119
|
+
if subquery.dig(:children).present?
|
|
120
|
+
# Send in the values at children as the subset hash and remove from existing subquery
|
|
121
|
+
child_nodes = subquery.delete(:children)
|
|
122
|
+
# If there are children, we recursively call this method again
|
|
123
|
+
# to build up the inner (child) queries first. This allows us
|
|
124
|
+
# to intelligently nest multiple levels of relationships.
|
|
125
|
+
commit_subset(query: subquery[:query], subset: child_nodes)
|
|
126
|
+
end
|
|
127
|
+
# If the subquery has a wrapper proc we are dealing with a compare to 0 situation
|
|
128
|
+
if subquery.dig(:wrapper).respond_to? :call
|
|
129
|
+
subquery[:query] = subquery[:wrapper].call(subquery[:query], subquery[:key], subquery[:secondary])
|
|
130
|
+
end
|
|
131
|
+
|
|
132
|
+
parent_table = subquery[:instance].active_record.arel_table
|
|
133
|
+
linking_key = subquery[:key]
|
|
134
|
+
inner_query = subquery[:query]
|
|
135
|
+
|
|
136
|
+
# Compare database connections for inner and outer query. Refer to ActiveRecord::Reflection
|
|
137
|
+
# Example:
|
|
138
|
+
# class Company < ActiveRecord::Base
|
|
139
|
+
# has_many :clients
|
|
140
|
+
# end
|
|
141
|
+
|
|
142
|
+
# Company.reflect_on_association(:clients).klass
|
|
143
|
+
# # => Client
|
|
144
|
+
|
|
145
|
+
# If the query needs to be flipped because it's a negative do that here
|
|
146
|
+
connecting_method = subquery[:inverse_clause] ? :not_in : :in
|
|
147
|
+
|
|
148
|
+
current_model = subquery[:instance]&.klass
|
|
149
|
+
parent_model = subquery[:instance]&.active_record
|
|
150
|
+
use_multiple_databases = (inner_query.is_a? Arel::SelectManager) && use_multiple_databases?(current_model, parent_model)
|
|
151
|
+
if query.present?
|
|
152
|
+
# If query exists and is a Select Manager we are deeply nested and need to build the query
|
|
153
|
+
# with a WHERE statement
|
|
154
|
+
if query.is_a? Arel::SelectManager
|
|
155
|
+
if use_multiple_databases
|
|
156
|
+
array_of_ids = current_model.connection.exec_query(inner_query.to_sql).rows.flatten
|
|
157
|
+
query.where(parent_table[linking_key.to_s].in(array_of_ids))
|
|
158
|
+
else
|
|
159
|
+
# Same DB, don’t decompose. Note: Where’s called on AREL select managers modify the object in place
|
|
160
|
+
query.where(parent_table[linking_key.to_s].in(inner_query))
|
|
161
|
+
end
|
|
162
|
+
else
|
|
163
|
+
# Otherwise we are joining nodes, which requires an AND statement (ORs are immediately commited)
|
|
164
|
+
# query can be a `Arel::Nodes::In` class (See HMMT test for example)
|
|
165
|
+
# The group() in front of query is required for nested relationship attributes.
|
|
166
|
+
if use_multiple_databases
|
|
167
|
+
array_of_ids = current_model.connection.exec_query(inner_query.to_sql).rows.flatten
|
|
168
|
+
query = group(query).and(group(parent_table[linking_key.to_s].in(array_of_ids)))
|
|
169
|
+
else
|
|
170
|
+
query = group(query).and(group(parent_table[linking_key.to_s].in(inner_query)))
|
|
171
|
+
end
|
|
172
|
+
# query = group(query).and(group(parent_table[linking_key.to_s].in(inner_query)))
|
|
173
|
+
end
|
|
174
|
+
else
|
|
175
|
+
# No existing query, top level of stack
|
|
176
|
+
if use_multiple_databases
|
|
177
|
+
array_of_ids = current_model.connection.exec_query(inner_query.to_sql).rows.flatten
|
|
178
|
+
query = parent_table[linking_key.to_s].send(connecting_method, array_of_ids.uniq)
|
|
179
|
+
else
|
|
180
|
+
query = parent_table[linking_key.to_s].send(connecting_method, inner_query)
|
|
181
|
+
end
|
|
182
|
+
end
|
|
183
|
+
end
|
|
184
|
+
query
|
|
185
|
+
end
|
|
186
|
+
|
|
187
|
+
def use_multiple_databases?(current_model, parent_model)
|
|
188
|
+
# Are the queries on different databases?
|
|
189
|
+
parent_model.connection_db_config.configuration_hash != current_model.connection_db_config.configuration_hash
|
|
190
|
+
end
|
|
191
|
+
|
|
192
|
+
def get_pending_relationship_subquery
|
|
193
|
+
get_pending_relationship_item("query")
|
|
194
|
+
end
|
|
195
|
+
end
|
|
196
|
+
end
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
<%
|
|
2
|
+
unless defined? stored_filters
|
|
3
|
+
stored_filters = false
|
|
4
|
+
end
|
|
5
|
+
%>
|
|
6
|
+
<div
|
|
7
|
+
data-controller="refine--search-filter"
|
|
8
|
+
data-refine--search-filter-search-frame-id-value="index_search"
|
|
9
|
+
data-refine--search-filter-submit-url-value="<%= refine_blueprint_path %>"
|
|
10
|
+
data-action="filter-submit-success@document->refine--search-filter#loadResults"
|
|
11
|
+
>
|
|
12
|
+
|
|
13
|
+
<%= tag.div class: "refine-filter-builder-dropdown-container", data: {
|
|
14
|
+
controller: "refine--toggle"
|
|
15
|
+
} do %>
|
|
16
|
+
<button
|
|
17
|
+
class="refine-filter-condition-toggle-button"
|
|
18
|
+
data-action="refine--toggle#toggle"
|
|
19
|
+
type="button"
|
|
20
|
+
>
|
|
21
|
+
<%= t('global.buttons.filter') %>
|
|
22
|
+
</button>
|
|
23
|
+
<div
|
|
24
|
+
hidden
|
|
25
|
+
data-refine--toggle-target="content"
|
|
26
|
+
data-reveal
|
|
27
|
+
data-transition
|
|
28
|
+
data-transition-enter="transition ease-out duration-100"
|
|
29
|
+
data-transition-enter-start="transform opacity-0 scale-95"
|
|
30
|
+
data-transition-enter-end="transform opacity-100 scale-100"
|
|
31
|
+
data-transition-leave="transition ease-in duration-75"
|
|
32
|
+
data-transition-leave-start="transform opacity-100 scale-100"
|
|
33
|
+
data-transition-leave-end="transform opacity-0 scale-95"
|
|
34
|
+
data-controller="filter-link"
|
|
35
|
+
|
|
36
|
+
class="refine-filter-builder-dropdown-search-box"
|
|
37
|
+
style="min-width: 50vw; max-height: 66vh; overflow: auto;"
|
|
38
|
+
>
|
|
39
|
+
<%= turbo_frame_tag :refine_filter_modal, loading: :lazy,
|
|
40
|
+
src: new_refine_blueprint_path(refine_filters_builder: {stable_id: @stable_id, filter_class: @refine_filter.class.name}, stored_filters: stored_filters.presence) do %>
|
|
41
|
+
|
|
42
|
+
<% end %>
|
|
43
|
+
<div class="refine-filter-builder-dropdown-button-container">
|
|
44
|
+
<button
|
|
45
|
+
class="refine-filter-builder-dropdown-cancel-button"
|
|
46
|
+
data-action="click->refine--toggle#toggle"
|
|
47
|
+
type="button"
|
|
48
|
+
>
|
|
49
|
+
<%= t('global.buttons.cancel') %>
|
|
50
|
+
</button>
|
|
51
|
+
<button
|
|
52
|
+
class="refine-filter-builder-dropdown-apply-button"
|
|
53
|
+
data-action="click->refine--search-filter#search"
|
|
54
|
+
data-filter-link-target="link"
|
|
55
|
+
type="button"
|
|
56
|
+
>
|
|
57
|
+
<%= t('global.buttons.apply') %>
|
|
58
|
+
</button>
|
|
59
|
+
</div>
|
|
60
|
+
</div>
|
|
61
|
+
<% end %>
|
|
62
|
+
|
|
63
|
+
</div>
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
<% if @refine_filter %>
|
|
2
|
+
<%= tag.div id: dom_id(@refine_filter_builder, "query"),
|
|
3
|
+
data: {
|
|
4
|
+
controller: "refine--filter-pills refine--state turbo-visit",
|
|
5
|
+
refine__state_conditions_value: @refine_filter_query.available_conditions_attributes.to_json,
|
|
6
|
+
refine__state_blueprint_value: @refine_filter_builder.blueprint.to_json,
|
|
7
|
+
refine__state_class_name_value: @refine_filter_query.configuration[:class_name],
|
|
8
|
+
refine__state_refresh_url_value: refine_blueprint_path,
|
|
9
|
+
refine__state_client_id_value: @refine_filter_builder.client_id,
|
|
10
|
+
refine__filter_pills_submit_url_value: refine_blueprint_path,
|
|
11
|
+
action: "filter-submit-success->turbo-visit#visit"
|
|
12
|
+
} do
|
|
13
|
+
%>
|
|
14
|
+
|
|
15
|
+
<% if @refine_filter.human_readable_criterions.any? %>
|
|
16
|
+
<div class="text-blue-600 pb-3 font-medium text-sm rounded w-full">
|
|
17
|
+
<div class="w-full flex pb-1 refine-filter-condition-pills-wrapper">
|
|
18
|
+
<div class="refine-filter-conditions-wrapper-icon"></div>
|
|
19
|
+
<% @refine_filter.human_readable_criterions.each_with_index do |condition, index| %>
|
|
20
|
+
|
|
21
|
+
<% if ["or", "and"].include?(condition) %>
|
|
22
|
+
<span class="refine-filter-condition-pill-and"><%= t(".pill_and.#{condition}") %></span>
|
|
23
|
+
<% else %>
|
|
24
|
+
<% data_action = "click->refine--filter-pills#delete" %>
|
|
25
|
+
<div class="refine-filter-condition-pill">
|
|
26
|
+
<%= condition %>
|
|
27
|
+
<button
|
|
28
|
+
class="refine-filter-remove-condition-pill"
|
|
29
|
+
data-action="<%= data_action %>"
|
|
30
|
+
data-criterion-id="<%= index %>"
|
|
31
|
+
>
|
|
32
|
+
</button>
|
|
33
|
+
</div>
|
|
34
|
+
<% end %>
|
|
35
|
+
<% end %>
|
|
36
|
+
</div>
|
|
37
|
+
</div>
|
|
38
|
+
<% end %>
|
|
39
|
+
<% end %>
|
|
40
|
+
<% end %>
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
<%
|
|
2
|
+
style ||= ""
|
|
3
|
+
|
|
4
|
+
case style
|
|
5
|
+
when "tr"
|
|
6
|
+
classes = "rounded-sm p-1 w-full mx-auto mt-0"
|
|
7
|
+
spacing = "2"
|
|
8
|
+
rows = 1
|
|
9
|
+
when "large"
|
|
10
|
+
classes = "rounded-md shadow p-12 w-full mx-auto mt-6"
|
|
11
|
+
spacing = "6"
|
|
12
|
+
rows = 5
|
|
13
|
+
else
|
|
14
|
+
classes = "rounded-sm shadow-sm p-4 w-full mx-auto mt-4"
|
|
15
|
+
spacing = "4"
|
|
16
|
+
rows = 3
|
|
17
|
+
end
|
|
18
|
+
%>
|
|
19
|
+
<div class="<%= classes %>">
|
|
20
|
+
<div class="animate-pulse flex space-x-<%= spacing %>">
|
|
21
|
+
<div class="rounded-full bg-gray-400 h-12 w-12"></div>
|
|
22
|
+
<div class="flex-1 space-y-<%= spacing %> py-1">
|
|
23
|
+
<div class="h-4 bg-gray-400 rounded w-3/4"></div>
|
|
24
|
+
<div class="space-y-<%= spacing.to_i / 2 %>">
|
|
25
|
+
<% rows.times do %>
|
|
26
|
+
<div class="h-4 bg-gray-400 rounded"></div>
|
|
27
|
+
<% end %>
|
|
28
|
+
<div class="h-4 bg-gray-400 rounded w-5/6"></div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</div>
|
|
32
|
+
</div>
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
<%
|
|
2
|
+
is_disabled = @refine_filter_builder.refine_filter.criteria_limit_reached?
|
|
3
|
+
criteria_limit = @refine_filter_builder.refine_filter.criteria_limit
|
|
4
|
+
%>
|
|
5
|
+
|
|
6
|
+
<%= tag.div(
|
|
7
|
+
class: "refine-add-container",
|
|
8
|
+
data: {
|
|
9
|
+
controller: 'refine--add',
|
|
10
|
+
refine__add_previous_criterion_id_value: (criteria.last.position unless is_disabled)
|
|
11
|
+
}) do %>
|
|
12
|
+
|
|
13
|
+
<%= tag.div(
|
|
14
|
+
class: class_names(
|
|
15
|
+
"refine-add-and" => true,
|
|
16
|
+
"refine-add-disabled" => is_disabled
|
|
17
|
+
),
|
|
18
|
+
style: "transition: all .15s ease",
|
|
19
|
+
data: {action: ("click->refine--add#criterion" unless is_disabled)},
|
|
20
|
+
title: (t("refine.inline.filters.criteria_limit", criteria_limit: criteria_limit) if is_disabled)
|
|
21
|
+
|
|
22
|
+
) do %>
|
|
23
|
+
<span class="refine-add-button-text"><%= t("refine.refine_blueprints.add_and.add_and") %></span><i class="refine-add-icon fal fa-plus"></i>
|
|
24
|
+
<% end %>
|
|
25
|
+
<% end %>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<%
|
|
2
|
+
is_disabled = @refine_filter_builder.refine_filter.criteria_limit_reached?
|
|
3
|
+
criteria_limit = @refine_filter_builder.refine_filter.criteria_limit
|
|
4
|
+
%>
|
|
5
|
+
|
|
6
|
+
<%= tag.div(
|
|
7
|
+
class: "refine-add-container",
|
|
8
|
+
data: {controller: 'refine--add'}) do %>
|
|
9
|
+
|
|
10
|
+
<%= tag.div(
|
|
11
|
+
class: class_names(
|
|
12
|
+
"refine-add-group" => true,
|
|
13
|
+
"refine-add-disabled" => is_disabled
|
|
14
|
+
),
|
|
15
|
+
data: {action: ("click->refine--add#group" unless is_disabled)},
|
|
16
|
+
title: (t("refine.inline.filters.criteria_limit", criteria_limit: criteria_limit) if is_disabled)
|
|
17
|
+
) do %>
|
|
18
|
+
<% if @refine_filter_query.grouped_criteria.any? %>
|
|
19
|
+
<%= t("refine.refine_blueprints.add_group.add_or") %>
|
|
20
|
+
<% else %>
|
|
21
|
+
<%= t("refine.refine_blueprints.add_group.create_filter") %>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|
|
24
|
+
<% end %>
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
<% prefixedClass = 'refine-clause-select-prefixed' if meta[:prefix] %>
|
|
2
|
+
<%= tag.div class: prefixedClass, data: {
|
|
3
|
+
action: "$change->refine--update#clause",
|
|
4
|
+
controller: 'fields--super-select',
|
|
5
|
+
fields__super_select_enable_search_value: 'true',
|
|
6
|
+
fields__super_select_container_width_value: 'resolve',
|
|
7
|
+
} do %>
|
|
8
|
+
<% if meta[:prefix] %>
|
|
9
|
+
<span class="refine-clause-select-prefix"><%= meta[:prefix] %></span>
|
|
10
|
+
<% end %>
|
|
11
|
+
<select
|
|
12
|
+
data-fields--super-select-target="select"
|
|
13
|
+
data-input-id='<%= input_id if defined? input_id %>'
|
|
14
|
+
name="clauses"
|
|
15
|
+
class="refine-clause-select"
|
|
16
|
+
>
|
|
17
|
+
<% meta[:clauses].each do |clause| %>
|
|
18
|
+
<option
|
|
19
|
+
value="<%= clause[:id] %>"
|
|
20
|
+
<% if selected_clause == clause[:id] %>selected<% end %>
|
|
21
|
+
><%= clause[:display] %></option>
|
|
22
|
+
<% end %>
|
|
23
|
+
</select>
|
|
24
|
+
<% end %>
|
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
<%
|
|
2
|
+
conditions = @refine_filter_query.available_conditions_attributes
|
|
3
|
+
|
|
4
|
+
conditions_for_category = -> (category) do
|
|
5
|
+
conditions.filter { |c| c[:meta][:category] == category}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
categories = conditions.map { |c| c[:meta][:category] }.uniq.compact
|
|
9
|
+
|
|
10
|
+
# Note that the stimulus controllers set default condition id for new conditions
|
|
11
|
+
# so this is only for rare cases where it gets unset
|
|
12
|
+
selected_condition_id ||= @refine_filter.default_condition_id
|
|
13
|
+
selected_condition_id ||= categories
|
|
14
|
+
.first
|
|
15
|
+
&.conditions_for_category.call(categories.first)
|
|
16
|
+
[:id]
|
|
17
|
+
|
|
18
|
+
uncategorized_conditions = conditions.filter { |c| c[:meta][:category].nil? }
|
|
19
|
+
|
|
20
|
+
%>
|
|
21
|
+
|
|
22
|
+
<%= tag.div data: {
|
|
23
|
+
action: "$change->refine--update#condition",
|
|
24
|
+
controller: 'fields--super-select',
|
|
25
|
+
fields__super_select_enable_search_value: 'true',
|
|
26
|
+
fields__super_select_container_width_value: 'resolve',
|
|
27
|
+
} do %>
|
|
28
|
+
<select
|
|
29
|
+
data-fields--super-select-target="select"
|
|
30
|
+
data-condition-id="<%= selected_condition_id %>"
|
|
31
|
+
name="conditions"
|
|
32
|
+
class="refine-condition-select"
|
|
33
|
+
>
|
|
34
|
+
<optgroup >
|
|
35
|
+
<% uncategorized_conditions.each do |condition_option| %>
|
|
36
|
+
<option
|
|
37
|
+
value="<%= condition_option[:id] %>"
|
|
38
|
+
<% if selected_condition_id == condition_option[:id] %>selected<% end %>
|
|
39
|
+
><%= condition_option[:display] %></option>
|
|
40
|
+
<% end %>
|
|
41
|
+
</optgroup>
|
|
42
|
+
<% categories.each do |category| %>
|
|
43
|
+
<optgroup class="divider" label="<%= category %>">
|
|
44
|
+
<% conditions_for_category.call(category).each do |condition_option| %>
|
|
45
|
+
<option
|
|
46
|
+
value="<%= condition_option[:id] %>"
|
|
47
|
+
<% if selected_condition_id == condition_option[:id] %>selected<% end %>
|
|
48
|
+
><%= condition_option[:display] %></option>
|
|
49
|
+
<% end %>
|
|
50
|
+
</optgroup>
|
|
51
|
+
<% end %>
|
|
52
|
+
</select>
|
|
53
|
+
<% end %>
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
<% criterion_id = criterion.position %>
|
|
2
|
+
|
|
3
|
+
<%= tag.div(class: 'refine-criterion-container', data: {
|
|
4
|
+
controller: 'refine--update',
|
|
5
|
+
refine__update_criterion_id_value: criterion_id,
|
|
6
|
+
position: criterion.position}) do %>
|
|
7
|
+
|
|
8
|
+
<!-- Select Condition -->
|
|
9
|
+
<div class="refine-criterion-condition-container">
|
|
10
|
+
<%= render partial: 'refine/blueprints/condition_select', locals: {
|
|
11
|
+
selected_condition_id: criterion.condition_id } %>
|
|
12
|
+
</div>
|
|
13
|
+
|
|
14
|
+
<!-- Select Clause -->
|
|
15
|
+
<div class="refine-criterion-clause-container">
|
|
16
|
+
<%= render partial: 'refine/blueprints/clause_select', locals: {
|
|
17
|
+
meta: criterion.meta, selected_clause: criterion.input[:clause]} %>
|
|
18
|
+
</div>
|
|
19
|
+
|
|
20
|
+
<!-- Render correct type of condition -->
|
|
21
|
+
<%= render partial: "refine/blueprints/clauses/#{criterion.component}", locals: {
|
|
22
|
+
criterion: criterion,
|
|
23
|
+
condition: criterion.condition, input: criterion.input, criterion_id: criterion_id, meta: criterion.meta, meta_clause: criterion.selected_clause_meta, input_id: nil } %>
|
|
24
|
+
|
|
25
|
+
<!-- Refinements -->
|
|
26
|
+
<% criterion.refinements.each do |refinement|%>
|
|
27
|
+
|
|
28
|
+
<div class="refine-criterion-refinement-container">
|
|
29
|
+
<%= render partial: 'refine/blueprints/clause_select', locals: {
|
|
30
|
+
meta: refinement[:meta], input_id: "input, #{refinement[:id]}", selected_clause: criterion.input.dig(:input, refinement[:id].to_sym, :clause) || {} } %>
|
|
31
|
+
</div>
|
|
32
|
+
|
|
33
|
+
<%= render partial: "refine/blueprints/clauses/#{refinement[:component].underscore}", locals: {
|
|
34
|
+
condition: refinement, input: criterion.input[refinement[:id].to_sym] || {}, criterion_id: criterion_id, meta: refinement[:meta], input_id: "input, #{refinement[:id]}", meta_clause: criterion.meta_for_refinement_clause(refinement), criterion: criterion} %>
|
|
35
|
+
<% end %>
|
|
36
|
+
<!-- End Refinements -->
|
|
37
|
+
|
|
38
|
+
<% end %>
|
|
39
|
+
|
|
40
|
+
<%= render 'refine/blueprints/delete_criterion', criterion_id: criterion_id %>
|
|
41
|
+
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
<%= tag.div class: 'refine-delete-container', data: {
|
|
2
|
+
controller: 'refine--delete',
|
|
3
|
+
refine__delete_criterion_id_value: criterion_id,
|
|
4
|
+
} do %>
|
|
5
|
+
<%= button_tag type: 'button', class: 'refine-delete-button', data: {
|
|
6
|
+
action: "click->refine--delete#criterion",
|
|
7
|
+
} do %>
|
|
8
|
+
<i class="refine-delete-criterion-icon ti ti-trash"></i>
|
|
9
|
+
<span class="refine-delete-criterion-label"><%= t("refine.refine_blueprints.query.buttons.remove_filter") %></span>
|
|
10
|
+
<% end %>
|
|
11
|
+
<% end %>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
<%= tag.div id: dom_id(@refine_filter_builder, "group_#{group_id}") do %>
|
|
2
|
+
<div class="refine-group-container">
|
|
3
|
+
<% criteria&.each_with_index do |criterion, index| %>
|
|
4
|
+
<div class="refine-group-criterion-container">
|
|
5
|
+
<%= render "refine/blueprints/criterion", criterion: criterion %>
|
|
6
|
+
</div>
|
|
7
|
+
<% if criteria.count > 1 && index < criteria.count - 1 %>
|
|
8
|
+
<p class="refine-group-and"><%= t("refine.refine_blueprints.add_and.add_and") %></p>
|
|
9
|
+
<% end %>
|
|
10
|
+
<% end %>
|
|
11
|
+
<%= render 'refine/blueprints/add_and', criteria: criteria %>
|
|
12
|
+
</div>
|
|
13
|
+
<% end %>
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
<%= tag.div id: dom_id(@refine_filter_builder, "query"), class: 'refine-query-container', data: {
|
|
2
|
+
controller: "refine--state",
|
|
3
|
+
refine__state_conditions_value: @refine_filter_query.available_conditions_attributes.to_json,
|
|
4
|
+
refine__state_blueprint_value: @refine_filter_query.blueprint.to_json,
|
|
5
|
+
refine__state_class_name_value: @refine_filter_query.configuration[:class_name],
|
|
6
|
+
refine__state_refresh_url_value: refine_blueprint_path,
|
|
7
|
+
refine__state_client_id_value: @refine_filter_builder.client_id,
|
|
8
|
+
refine__state_default_condition_id_value: @refine_filter_query.filter.default_condition_id,
|
|
9
|
+
refine__state_validate_blueprint_url_value: validate_refine_blueprint_path } do %>
|
|
10
|
+
<%= content_tag :div, class: "refine-query-content-container" do %>
|
|
11
|
+
<div
|
|
12
|
+
class="refine-query-loading-container <%= "hidden" unless action_name == "create" %>"
|
|
13
|
+
id="refine-loader"
|
|
14
|
+
data-refine--state-target="loading">
|
|
15
|
+
<p class="refine-query-loading-text"><%= t("refine.refine_blueprints.query.loading") %></p>
|
|
16
|
+
</div>
|
|
17
|
+
|
|
18
|
+
<%= tag.div id: dom_id(@refine_filter_builder, "groups") do %>
|
|
19
|
+
<% @refine_filter_query.grouped_criteria.each_with_index do |criteria, index| %>
|
|
20
|
+
<% if index > 0 %>
|
|
21
|
+
<p class="refine-query-or"><%= t("refine.refine_blueprints.query.query_or") %></p>
|
|
22
|
+
<% end %>
|
|
23
|
+
<%= render partial: 'refine/blueprints/group', locals: {
|
|
24
|
+
criteria: criteria,
|
|
25
|
+
group_id: index }
|
|
26
|
+
%>
|
|
27
|
+
<% end %>
|
|
28
|
+
|
|
29
|
+
<% end %>
|
|
30
|
+
|
|
31
|
+
<%= render 'refine/blueprints/add_group' %>
|
|
32
|
+
<% end %>
|
|
33
|
+
<% end %>
|
|
34
|
+
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
<%= turbo_frame_tag "stored_filters" do %>
|
|
2
|
+
<%= content_tag :div, class: "refine-stored-filter-head-container" do %>
|
|
3
|
+
<h3
|
|
4
|
+
class="refine-stored-filter-header"
|
|
5
|
+
>
|
|
6
|
+
<%= @stored_filter&.name || t("global.buttons.filter") %>
|
|
7
|
+
</h3>
|
|
8
|
+
<a
|
|
9
|
+
href="<%= refine_stored_filters_path(@refine_filter_builder.to_params) %>"
|
|
10
|
+
class="refine-stored-filter-link"
|
|
11
|
+
>
|
|
12
|
+
<%= t('global.buttons.find_filter') %>
|
|
13
|
+
</a>
|
|
14
|
+
<span class="text-xs ml-4 mr-4 leading-6">
|
|
15
|
+
<a
|
|
16
|
+
class="text-blue-600 background-transparent font-bold underline outline-none focus:outline-none"
|
|
17
|
+
href="<%= new_refine_stored_filter_path(@refine_filter_builder.to_params) %>"
|
|
18
|
+
>
|
|
19
|
+
<%= t('global.buttons.save_filter') %>
|
|
20
|
+
</a>
|
|
21
|
+
</span>
|
|
22
|
+
<% end %>
|
|
23
|
+
<% end %>
|